普通视图

发现新文章,点击刷新页面。
昨天以前四火的唠叨

关于近期求职的近况和思考

2025年2月21日 05:23

自去年秋天裸辞之后,一直在考虑职业生涯的问题。之后加入求职大军,目前进展还算顺利,作为软件工程师的下一站也将很快确定下来。但是这一次的 career break,虽说时间不算长,却给了我莫大的启发,我也有了一些思考。

从 fullstack engineer 到 platform engineer

其实在去年年初的时候就简要叙述过这个事情。熟悉我的朋友都知道,我的职业生涯有点奇怪,从 Huawei 开始,我是一个全栈工程师(fullstack engineer),从网页设计、前端开发到后端开发都是一锅端的,当时也非常喜欢这个方向,这也是我后来在极客时间上写 《全栈工程师修炼指南》这门课的原因之一。

不过后来这个兴趣点也在慢慢迁移,在加入 Amazon 之后,我陆续经历了两个大的 data platform 团队,一个是做销量预测(demand forecasting)的,一个是为 retail 一侧计算成本和利润的。在这两个 team 中,都要和大数据打交道,和 scientists 和 analysists 一起合作,而我作为一个 engineer 的基础工作,就是把 infra 维护好,提供好用的工具让他们的问题观测和分析更简单。也是从 Amazon 开始,我开始更关注一个模糊的目标,一个可以持续建设的 platform,关注一个 solution stack,而不是具体某个 service,或者某个具体技术。

差不多六年之后,在 Oracle,我带领的 team 则是侧重于 infra 了,依然是作为 engineer,主要为 cloud 管 datacenter 的两个东西,一个是 process automation,一个是 matadata storage。在这个比较大的 team 我获得了比较大的职业生涯成长,我们 own 一个非常完整的 solution stack,也越来越确定我关注的重点,以及未来发展的方向。虽然从一定意义上来说,做的事情依然是 full stack 的,但我开始更多地称呼自己 platform engineer,而不再是 fullstack engineer 了。

之后在 2022 年加入了 Doordash,从巨头转向更加敏捷的中型互联网公司,一开始在一个偏向于 infra 的团队,做 gateway platform,我还是比较享受这一年多的时间的。当时 team 里面有一个非常有经验和见解的工程师,我从他身上学到不少。后来因为 org 调整的原因,我选择抓住机会去做了很短一段时间的产品,回头看这个决定有些鲁莽,但至少也确认了一件事情,单纯做产品并不是我最喜欢和擅长的。

对于下一站,我的几个在考虑的选项中,无疑都是偏向于 platform 和 infra 的 team,其中有两个机会我尤其感兴趣,其中一个是维护开源的高并发 library 的,还有一个是做 AI infra 的。现在我正在努力做的功课,就是把它们前前后后都了解清楚,然后做出自己的选择。

AI 将替代软件工程师吗

这是个很好的问题。只不过,这个 “将” 可以斟酌,因为它已经替代一些初级的工程师工作了。但放眼未来,它到底能替代多少工程师的工作,我不知道。现在,很显然的有两件事:

  1. 软件工程师的很多工作确实是可以替代的,它们也正在被替代,包括某些设计和编码。
  2. 软件工程师这样相对有门槛的工作都可以被 AI 替代,更何况那些门槛相对低的工作呢?

但是关于上面这第 1 点,这样的 “替代” 到底能达到多深的地步,我不知道。我隐约觉得,能被替代的工作往往是非常具体,逻辑比较确定和简单,而且不需要处理人际交流和关系的工作。以前有人觉得,AI 不能替代艺术家的工作,因为他们的工作是创造性的。可是你现在看看呢,写作、谱曲、绘画,都变得可能了,可是我并不想反驳这条观点,而是想说,这从一定的角度上来看,我们是不是可以说,艺术家们的工作,其实也并不全是创造性的呢?

而关于上面这第 2 点,有更多岗位要远比软件工程师更值得担忧,而软件工程师们,只不过是因为现在站得和 AI 更近,替代后的成本节约更多,因而更焦虑。就如同软件行业是经济的风向标一样,当工程师们开始焦虑,不久的将来整个社会都会焦虑。从好的一面看,当工业革命开始,无数人担心机器代替人类工作,但最终机器却为人类创造了更多的工作,我想这一次机器替换成了 AI,道理也一样。无论如何,不要逃避,而要尝试改变和拥抱这样的变化,因为这个趋势是不以人的意志为转移的,该来的总会来。

我觉得,总体来看,AI 将很快替代的,未必是工作,而是特定领域的技能。我觉得这句话里面,有两个重点,一个是 “技能”,一个是 “特定领域”。同一份工作,也许需要能力和技术将大不相同。对于一个需要做出复杂判断的工作,并且这个工作还需要许多不同领域视野和经验积累的,AI 相对会更难替代。

对于一些传统行业而言,那里有更多的固化、低效、不愿革新和进取的工作。我有个朋友在保险行业,做的事情就是要用科技(不仅仅是 AI)来变革,把保险公司从传统上认为人力资本巨大的企业变成一个靠软件来横向扩张的 SaaS(软件即服务)公司。趁这个 job hunting 的机会,我也去了解了一番。我觉得,这些看似红海的传统行业实则是使用软件革新的蓝海,未来会有更多的 SaaS 公司。有很多这样的传统领域,成长缓慢,或者利润率低,资本不太看得上,但是从这个角度思考,或许有大的机会。

如今的就业市场怎样

在刚离职的时候,我曾经提到过对于就业市场的理解。大致来说,就是比我 2022 年下半年那会略好,但是想要回到疫情前那种 “无比风光” 的状态是不可能了。现在回头看,在经过了一番求职的折腾后,我可以说,这种观点还是大致正确的,不过就业市场比我最初想的,还是要好不少。简单说来,我觉得近期软件工程师的机会,比 2022 年下半年要多不少。

其次,一个萝卜一个坑。我记得 2017 年那会找工作的时候,我可以先把 phone screen 搞定,然后排一堆 onsite 在同一周并行,这样的话一旦我拿到 offer,如果需要选择的话比较容易操作,因为它们的时限都比较接近。但是这次好几家公司都是过了 phone screen,然后告诉我坑已经被填了。所以之前并行的策略没有那么有效了,看到心仪的职位,不仅需要面试得好,还需要尽快完成。

再次,bar 还是很高。有时候看到很多软件工程师朋友还在谈论刷题的话题,其实刷题是必要条件没错,但是离实际需要差太远了。从分配时间的角度,还是需要更多时间分配到其它环节去。总体来说,就算两轮 ps 加上 5 轮 onsite 的话,ps 全都要 positive,onsite 全都要 positive,也许最多一轮 on boundary,否则基本就挂了;有些情况下,就算全是 positive,如果不够 strong,还是会 downlevel。所以,总体来看 bar 还是比较高的。行业发展就是这样的,软件业也不是例外,求职门槛提高,这是行业成熟的一个标志。

最后,回头看,去年的这个裸辞还是果断(或者武断)的,但是回想起来,如果再给我一次机会,我估计还是会做出同样的选择。没有什么对错,就是做出自己的选择而已。这段 break 的时间我还是比较享受的,而且除去 career 发展的目的以外,由于再在 job market 上面走一遭,起码从面试的角度来说,有了比较新鲜的认识,哪一天如果被裁员,我相信也不会过度慌乱。这也算是一个额外的收获吧。

我知道有很多朋友和我一样,近期在求职。这个过程很辛苦,也可能有磕磕绊绊,希望大家都能保持自信,或长或短的时间,找到自己理想的职业生涯下一站。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

聊一聊分布式系统中的时间

2024年10月21日 08:41

今天聊一下时间的话题。在分布式系统中,“时间” 是一个挺有趣,但是很难处理的东西。我把自己的理解简单整理下来。

不可靠的物理时钟

首先,单一节点的物理时钟是不可靠的。

物理时钟本身就有偏差,可是除此之外,可以引起节点物理时钟不准确的原因太多了,比如 clock jump。考虑到 NTP 协议,它基于 UDP 通信,可以从权威的时钟源获取信息,进行自动的时间同步,这就可能会发生 clock jump,它就是说,时钟始终会不断进行同步,而同步回来的时间,是有可能不等于当前时间的,那么系统就会设置当前时间到这个新同步回来的时间。即便没有这个原因,考虑到数据从网络传输的延迟,处理数据的延迟等等,物理时钟是非常不可靠的。

如果一个分布式系统,多个节点想要仅仅依赖于物理时钟来完成什么操作,那么只能祈祷运气足够好了。在 《从物理时钟到逻辑时钟》这篇文中,我已经介绍了对于物理时钟不可靠的问题,我们有一个解决的办法,就是引入 Lamport 逻辑时钟,或者使用向量时钟,这里就不赘述了。

超时

分布式系统中什么样的执行结果最难处理,成功还是失败?其实都不是,最难处理的结果是超时,因为执行超时了,但是系统却并不知道它:

  • 是没执行,
  • 是执行成功了,
  • 还是执行失败了。

所谓超时,一个显然的问题是,超过多少时间才算超时?往往没有一个公式,更没有一个标准答案,我觉得《Designing Data-Intensive Applications》这本书里面对这一点总结得很好——对于超时时间的定义,其实就是一个 tradeoff,一个基于 failure detection delay 和 risk of premature timeouts 之间抉择的平衡。如果超时时间设置长了,就会减少超时判定的误杀,但是对于系统失败的识别就会延迟;反之,如果超时时间设置过短,那就会触发更多看起来是超时的 case,但是它们本身其实并没有真正超时。

通常来说,对于超时的处理,其实办法也不多。一种是放弃,一种是重试。就像消息投递,如果要保证 “至多投递一次”,那在投递超时后,就直接放弃;如果要保证 “至少投递一次”,那在投递超时后,就重试。如果要重试,那就需要引入保证幂等性的机制。

分布式事务 SAGA 对于超时的处理,其实也是遵照上面的原则,在系统内单步都成为事务的基础上,把流程视作一个状态机,无论单步操作是成功还是失败,都会根据清晰的预定义逻辑,触发相应的正向流程或者反向流程,可是唯独超时,多数情况下最有意义的事情就是重试,也只能重试,因为谁也不知道它究竟实际是成功了还是失败了。

说完操作超时,再来说一下节点超时。很多分布式系统中都会使用一种 lease(租约)的机制,比如一个集群中的 leader,作为 leader 会扮演不同的角色,但是必须要 renew 这个 lease,否则超过一定的时间,无论它给不给响应,它都会被开除出 leader 的角色,而 follower 会重新选举(或者其他方式)一个 leader。

比较难处理的是,如果这个节点本来是被 hang 住了,导致了超时,它也已经被踢下 leader 的角色,但是之后它 “活” 过来了(比如经过了一个超长时间的 GC),它还以为自己是 leader,继续去干 leader 干的事,变成了一个假 leader。这其实就是出现了脑裂,本质上是一个一致性的问题。这种情况比较难处理,因为即便有 heartbeat 不断检测,在每两个 heartbeat 的间隙,可能这种重要的变动就发生了。

要解决这种问题,需要使用 token fence 的方法,即让每次最关键的状态数据的更改,携带一个单调递增的 token,这种情况下这个假 leader 发起更改的 token,已经小于系统中最新的 token 了,接收这个数据更改的子系统应该拒掉这个请求。上面说的节点超时的情况我在《谈谈分布式锁》里面有详细说明。

计算机的两种时钟

有两种时钟是计算机普遍支持的,一种叫做 time-of-day clock,就是我们一般意义上的时钟,代表着相对于 1970 年 1 月 1 日的 epoch 时间,也就是 Java 里面 System.currentTimeMillis() 返回的。网络时间协议 NTP 就是用来同步计算机这个时间的。

不过,其实还有一种时钟,叫做 monotonic clock(单调时钟),在 Java 里面相应的接口是 System.nanoTime()。这个时钟有一个特点,就是它永不回头。对于 time-of-day clock 来说,时间是可能 “回头” 的,对于很多应用来说,时间回头是要命的。不过这个时钟给出的具体时间却是毫无意义,如果在不同的机器上调用 System.nanoTime(),会得到完全随机的结果。API 的名字是纳秒,可是这个时钟并不给出到纳秒的时间精度,它的作用是用来帮助计算间隔时间的:在同一个节点,第二次调用的时间减掉第一次调用的时间,这个结果(时间间隔)是严格递增(不回头)的。从这个意义上说,除去时间这个视角本身,这个时钟更像是一个单调的计数器。既然是单调的计数器,就可以用来帮助产生系统严格自增的 ID。

下面是 System.nanoTime() Javadoc 上面的解释:

Returns the current value of the most precise available system timer, in nanoseconds.
This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how frequently values change. Differences in successive calls that span greater than approximately 292 years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.

TrueTime

一般来说,我们都知道计算机的时钟有误差,可是这个误差是多少,差 1 毫秒还是 1 分钟,并没有任何严格保证。绝大多数接触到的时间 API 也是如此,可是,Google 数据库 Spanner 的 TrueTime API 却。它使用了 GPS 时钟和原子钟两种完全独立的机制来冗余某一个机制失败导致的时钟问题,增加 reliability。此外,它还有和 System.nanoTime() 一样的严格递增的特点。

它有三个核心 API,很有意思:

  • TT.now() 它返回 [earliest, latest] 这样一个范围,表示当前时间就在这个范围内;
  • TT.after(t) 它返回当前时间是不是肯定在 t 之后
  • TT.before(t) 它返回当前时间是不是肯定在 t 之前

有了 TrueTime,这让分布式系统中,本来无法通过物理时钟解决的问题也变得可解决了。比如对于操作冲突的问题,现在的新办法就是等一个 buffer 时间,TrueTime 认为已经前一个操作的结束时间肯定已经过去了,再来启动后一个操作。当然,这个方法的缺点是 throughput,因为整个操作周期因为 buffer 时间的关系变长了。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

本地部署 Minikube + Docker 记录

2024年9月27日 06:52

我有 Mac 和 Windows,这些年折腾软件方面的环境 Linux 用得比较多,最近想安装一个 Kubernetes 的本地环境,本着 “生命不息,折腾不止” 的精神,打算在 Windows 上动手。了解到可以尝试 Minikube,在此简单记录一下。

首先得要安装 Docker,但是在 Windows 下跑 Docker 有两种方式,WSL(Windows Subsystem for Linux)或者 Hyper-V。我首先把这些 Windows 组件都勾上:

我两条路都去走了一下,为了使用 Hyper-V,我还去 BIOS 里面打开虚拟化支持的选项。不过,后来才知道,因为操作系统版本是 Windows 10 Home,虽说 Windows 上面跑 docker 有两种方式:

  • 一种是使用 WSL
  • 另一种是使用 Hyper-V

但在 Windows 10 Home 版本上只支持第一种。由于 Hyper-V 本质上是额外的虚拟机,而 WSL 更新,是虚拟化的 Linux 环境,是 Windows 操作系统原生支持的,性能要好一些。

其实,在 Docker 的设置里面也有说了:

可以列出所有 WSL(我使用的 WSL 2)目前支持的 Linux Distributions:

wsl --list --online

可以选一两个自己熟悉的安装了体验一下:

wsl --install Ubuntu
wsl --install Debian

整个 Windows 的文件系统都可以以 Linux 的方式访问。以往我一般在 Windows 上运行 Linux 命令都是使用 Cygwin 的,但是现在我了解到两者很不相同,WSL 是真正的虚拟化 Linux 环境,而 Cygwin 只不过把一些 Linux 命令编译成 Windows 的二进制版本。

安装 Minikube 和相关工具,配置环境变量。这次学到了可以用 Chocolatey,它是 Windows 下的软件安装工具。比如:

choco install kubernetes-cli

Minikube 可以以 VM、container,甚至 bare metal(Windows 下不支持)的方式来运行,通过 driver 参数指定。

我们使用 docker,这也是官方认定 preferred 的一种方式。这种方式下,Minikube 应用本身会作为 Docker container 跑在 Docker 里面(driver 的含义),同时,Minikube 也会使用 Docker 来跑其它的应用 container。

minikube config set driver docker
minikube delete
minikube start --driver=docker

看一下状态:

Done! kubectl is now configured to use “minikube” cluster and “default” namespace by default

看一下状态:

minikube status
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

此外,检查一下 WSL 已经安装的 Linux subsystem,能看到:

wsl -l
Windows Subsystem for Linux Distributions:
Ubuntu (Default)
docker-desktop
Debian

在 Docker 的 UI 上,也能看到:

跑起来以后,用 kubectl 验证一下:

kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:57514

查看所有 namespace:

kubectl get pods –all-namespaces

启动 dashboard:

minikube dashboard

接着,创建和部署一个 hello minikube 的 service:

kubectl create deployment hello-minikube --image=kicbase/echo-server:1.0
kubectl expose deployment hello-minikube --type=NodePort --port=8080
kubectl get services hello-minikube

通过访问:

minikube addons list

可以列出一堆可以立即安装的 addon,有一些是 K8s 的,有一些是 minikube 的,比如:

minikube addons enable auto-pause

这个可以在一段时间没有使用的情况下,暂停 K8s。

还有一个 addon 能让 dashboard 的 metrics 显示更多信息:

minikube addons enable metrics-server

后来,一通折腾以后发现,原来 Docker 的 settings 里面已经有了一个 Kubernetes 选项:

原来新版本的 Docker 里面自带了一套 K8s,它是完全跑在 Docker instance 里面,并且无法配置的,主要用于本地测试。它的运行也是基于 WSL。

现在就试一下,停掉 Minikube:

minikube stop

UI 确认确实停了(或者 docker ps):

然后把 Docker 的 Kubernetes tab 上面的两个选项都勾上,apply & restart。

不过等了好久,似乎卡在这一步了,我 reset 并且更新 Docker 以后,问题解决。打开以后我看到 Docker 启动了一堆 container。确认跑起来也没问题:

kubectl get nodes
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 10m v1.30.2

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

几个有意思的分布式系统设计模式

2024年10月2日 08:46

分布式系统有它特有的设计模式,无论意识到还是没有意识到,我们都会接触很多,网上这方面的材料不少,比如 《Catalog of Patterns of Distributed Systems》,还有 《Cloud Design Patterns》等等。这里简单谈谈几个我接触过的,也觉得比较有意思的模式。

LSM Tree

对于这个话题,基本上第一个在我脑海里蹦出来的就是 LSM 树(Log Structured Merge Tree)。其实,LSM 树本来只是指一种数据结构,这种数据结构对于大吞吐量的写入做了性能上的优化(比如日志写入),同时对于根据 key 的读取也有不错的性能。换言之,对于读写性能的平衡,大幅优化了写入,而小幅牺牲了读取,现在它也不再被局限于数据结构本身,而是泛化为能够提供这样特性的一种机制。

整个写入过程分为两个部分,为了追求极致的写入速度,写入方式都被设计成追加的:

  1. C0:这一层其实就是内存中写入的 buffer,它的实现可以用 RBTree,或者是 SkipList,这种树状有序的结构,它的写入和范围查询都很快。在 Bigtable 的设计中,它被称为 Memtable。
  2. C1:这一层则主要待在磁盘上了,它可以由若干个文件组成,每一个文件都是有序的数据,这样拿着一个 key 去文件查询对应的数据,根据 indexed keys 可以用二分法来查找。在 Bigtable 的设计中,它就是 SSTable。

所以,为了追求写性能,数据写入会直接插入到 C0 中,一旦 C0 达到一定大小,就会建立一个新的 C0’ 来替代旧的,而原有的 C0 会被异步持久化成 C1 中的一个新文件(其实就是做 snapshot);C1 中的文件全都是有序的,它们会不断地被异步 merge,小文件不断被合并成大文件(下图来自维基百科)。极端情况下,同一个 key 可以有若干次更新,并且更新能同时存在于 C0 和 C1 所有的文件中。

对于根据 key 的查询,需要先去 C0 中找,如果找到了最好,没找到的话需要去 C1 中找,最坏的情况下需要找每个文件。如果数据存在于多个地方,数据采用的优先级是,C0> 新的 C1 文件> 旧的 C1 文件。

对于不存在 C0 中的数据查询,为了尽量避免去每一个 C1 的文件中查询,Bigtable 会使用 bloom filter 来做第一步的存在性判断(校验用的数据全量加载在内存中),根据结果,如果这一步判断通过,这意味着数据可能存在于目标文件;如果没通过,这意味着数据肯定不存在于目标文件。

Write-Ahead Log

顺着 LSM Tree 的话题,说到 WAL。WAL 适用于解决这样一个问题:一个系统对于写请求有较苛刻的延迟或者吞吐量的要求,同时又要严格保证 durability(数据不丢)。

因此直观上,WAL 包含三步:

  1. 首先要求 append 日志,日志是持久化的,并且可以根据一致性的要求持久化到不止一个存储中
  2. 接着就是把数据更新到内存的某个数据结构中(比如上面的 LSM Tree 在内存中的结构 C0),这时候同步的请求响应就可以发回客户端了
  3. C0 的数据会异步 merge 压缩到持久性存储 C1 中(LSM Tree 部分的操作)

基本上思路就是把能延迟的操作全延迟了,如果服务端挂掉了,根据持久性存储+日志就可以完全恢复到挂掉之前的状态,因此数据不会丢。

于此,有一系列相关的 pattern,比如:

  • Low-Water Mark,低水位线:指的是这条线以前的数据全部都以常规方式持久化了(C1),因此如果节点挂掉的话,只需要根据现有的文件+低水位线以上的日志就可以完全还原挂掉之前的状态,也就是说,理论上低水位线以前的日志可以删掉了;
  • High-Water Mark,高水位线:日志是持久化的,但是这个持久化需要在多个节点上发生,这条线就指向了最新一条已经成功持久化到 “大多数” 节点上的日志(这个大多数其实就是 Majority Quorum 模式)。
  • Hinted Handoff,提示性移交,它和 WAL 有一定类似性,因此经常被放在一起比较:如果一个节点挂掉了,那么更新会跑到另外一个(随机的)节点上面去,等到挂掉的节点恢复的时候,这些数据会被重新写入那个恢复的节点,执行 replay 从而恢复一致性。

Clock Bound Request Batch

Request Batch 太常见不过了,请求可以批量发送,减少 overhead,从而减少资源(网络带宽、序列化开销等等)的消耗。通常的 batch 是根据大小或者数量来划分批次的,但是修饰词 Clock Bound 指的是,这样的分批还要依据时间,就是说,系统可以等待一段时间,这一段时间内的请求都可能打包成一个 batch,但是这样的打包还要有时间限制,到了这个时间,无论当前的 batch 有多小,都必须要发送出去了。

Kafka 客户端就有这样的一个机制,message 可以被 group,但是:

  • batch.size 这个参数指定 batch 最大有多少;
  • linger.ms 这个参数指定了最多等多久。

还有一些流处理系统也采用这种方式,比如 Spark 的 micro batching。

Singular Update Queue

Singular Update Queue 非常有用,queue 本身就是用来处理异步的事件,可以有若干个 producer 产生消息到队列里面,有若干个 consumer 来处理它们。这种场景下这个 queue 为核心的机制扮演了至少这样几个角色:

  1. 缓冲,平滑请求的波峰;
  2. 流控,保护下游系统不被负载冲垮;
  3. 校验,能写入 queue 的数据一定是符合格式的,从 queue 读取的数据也一定是符合格式的;
  4. 解耦,对于 MxN 的调用关联关系,所有的 producer 只和 queue 打交道(写入),而所有的 consumer 也之和 queue 打交道(读取),这样,复杂度就变成了 M+N。

对于写请求,我们需要保证这些事件处理不会有并发的问题,通过采用 Singlar Update Queue,对特定的 topic,我们可以设计一个良好的 sharding 规则,加上对于每一个 sharding(在 Kafka 等系统里面我们叫做 partition),设置为只有一个 consumer 线程,这样的话就保证了不会有并发问题,因为只有一个线程来处理所有这个 sharding 的消息,这种方式可以简化系统,不需要引入第三方锁系统就可以处理同一个 sharding 之间存在并发冲突的消息。

我想起另外一个相关的话题,monolith(单体应用)还是 microservices(微服务),一直是一个争论。早些时候,在微服务概念刚提出的时候,它受到了追捧,但是现在出现了越来越多批评的声音。一个突出的微服务的问题就是各个微服务之间像蜘蛛网一样复杂调用依赖的问题。而这样的问题,其中一个解决办法就是引入这样的 queue 在中间解耦。

有些时候,queue 里面未必存放全部完成 update 所需的数据,而是只放很少的内容,比如只有一个 key,consumer 拿到这个 key 以后去别的 service 获取完成任务需要的信息,因此这个 queue 就起到一个通知的作用。这其实就是 Claim Check 模式了。

Asynchronous Request-Reply

Asynchronous Request-Reply 本身是一个简单而且常用的机制,就是请求发起以后,服务端响应说,请求任务正在处理中,并返回给 client 一个 token。后续 client 拿着这个 token 就可以来(可以是另外一个单独的用于状态查询和结果获取的服务)查询请求的处理状态(poll),同时,服务端也可能会通知(push)客户端情况。

不过,既然上面谈到了 Singular Update Queue,它们俩有时是有关联的。

在使用 Singular Update Queue 的时候,如果 consumer 处理一个消息需要花很长的时间,那么它就可能成为整个系统吞吐量的瓶颈。很多时候,这个 consumer 花很长时间来处理往往不是因为有复杂的 CPU 计算,而是等待,比如等待一个远程调用结束,等待一个文件写入结束等等。

对于这样的问题,有两种解决思路:

  1. 一种是划分出更多的 partition,这样就可以设置更多的 consumer 线程来处理,但是这种方法带来的代价是更高的 overhead。
  2. 第二种是把处理流程变成异步执行的,而把它变成异步的就意味着系统没法直接妥善处理异步处理结果(它可能成功,也可能失败),因此对于这样一个子问题,有两个进一步解决的思路:
    1. 触发处理流程之后,预约下一个 “回访”,也就是采用这里提到的 Asynchronous Request-Reply 这样的机制,但是这个回访需要在一定时间后发生,并且一定要发生,因此这需要一个高可用的定时任务的服务(需要支持重试策略和失败处理),或者是封装一个 delay queue,即里面的消息需要过一段时间才能被 poll 到,因此这个机制有可能会比较复杂。
    2. 还有一个思路则是让 consumer 来集成 Coroutine,这样一个 consumer 线程可以被分享同时处理若干个消息,这些消息都来自同一个 partition(有一些开源库就专干这个事儿),于是这样多个消息就可以被同一个线程以非阻塞的方式并行处理。这个方法的一个局限性是,这些消息在 Coroutine 框架内处理时,不能存在并发冲突的问题。

Rate Limiting & Throttling

最后比较一下 Rate Limiting 和 Throttling。我也是不久前才区分清楚,以前我基本是把它们混在一起使用的。它们都是用来限流的,并且有多重不同的方式,可以是基于 fixed window,sliding window 等等。

但是它们的区别,本质上是它们工作的角度不同。Rate Limiting 是从 client 的角度来管理资源的,比如说,规定某一个/每个用户对于资源的访问不能超过一定的限度,因为资源不能让一个客户端全占了,这样其他人才可以有访问资源的权利;而 Throttling 则是从 service 的角度来管理资源的,比如说,规定某个/每一个 API 的访问 throughput 上限是多少,一道超过这个限度,请求就会被拒掉,从而保护服务。

和这两个可以放在一起的,其实还有一个 Circuit Breaker,不过 Circuit Breaker 的功能和这两个有较大区别,不太容易弄混,因此就不赘述了。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

谈谈分布式锁

2024年9月19日 07:33

不要使用分布式锁

就像 Martin Fowler 说的那样,“分布式调用的第一原则就是不要分布式”,谈分布式锁也要先说,不要使用分布式锁。原因很简单,分布式系统是软件系统中复杂的一种形式,而分布式锁是分布式系统中复杂的一种形式,没有必要的复杂性就不要引入。

有的逻辑是没有副作用的(纯函数代码),那就可以无锁执行;有的数据经过合理的 sharding 之后,可以使用单线程(单节点)执行,那就单线程执行。

比如一种常见的模式就是使用 queue(比如 Kafka),任务全部放到队列中,然后根据 sharding 的逻辑,不同的 consumer 来处理不同的任务,互相之间不会干扰冲突。

还有一个例子是 Kotlin Coroutine,通过指定一个单线程的 dispatcher,也可以保证它执行的操作之间互相不会有多线程的冲突问题。

有了这样的原则以后,再来谈谈几种分布式锁。

数据库锁

分布式系统中,我觉得我们最常见的锁就是使用一个中心数据库来做的。

一种是悲观锁,就是 “select xxx … for update” 这样的,相应的数据行会被锁上,直到 commit/rollback 操作发生。如果被别人锁了,当前线程没得到锁的话就会等着。

还有一种是乐观锁,就是使用版本号,“update … where … version=A” 这样的。如果 update 成功,表示获取锁成功,并且操作也成功;否则就是 update 失败,需要重新获取状态再来操作一遍。

大多数情况下,后者要更高效一些,因为阻塞的时间通常更短,不过在锁竞争比较激烈的情况下,反而效率会反过来。另外一个,悲观锁代码写起来会容易一些,因为 select 语句执行和 commit/rollback 是两步操作,因此二者之间可以放置任意逻辑;而乐观锁则是需要把数据的写操作和 version 的比较放在一条语句里面。

这两种都很常见,基本上我接触过的一半以上的项目都用过两者。这个数据库不一定非得是关系数据库,但是强一致性必须是保证的。

S3

使用 S3 来创建文件,让创建成功的节点得到锁,文件里面也可以放自定义的内容。我们去年的项目用到这个机制。这种方式是建立在 S3 2020 年 12 月 1 日,上线的 strong consistency 的 feature

大致上,有这样两种思路:

  1. 使用 S3 versioning,就是说,在 versioning 打开的情况下,文件的写入不会有 “覆盖” 的情况发生,所有内容都会保留。在创建文件的时候,response 种会有一个 x-amz-version-id header。节点写入文件后,再 list 一下所有的 version,默认这些 version 会根据创建的时间顺序递减排列,后创建的在前,因此比较其中最早的那个 version 和自己创建文件后得到的 version,如果两者相等,说明自己得到了锁。
  2. 使用 S3 Object Lock,这个可以控制让第一次写成功,后面的操作全部失败,所以第一次写入成功的节点得到锁。

使用这种方式,对于那些本来就需要使用 S3 文件系统来共享任意信息的情况很方便,但是需要自己处理超时的问题,还有 retention 策略(该不该/什么时候删掉文件)。

Redlock

Redlock 就是 Redis 的锁机制。Martin Kleppmann(就是那个写《Design Data-Intensive Applications》的作者)几年前写过一篇文章,来吐槽 Redlock 在几种情况下是有问题的:

  1. Clock jump:Redlock 依赖于物理时钟,而物理时钟有可能会跳(jump),并且这种状况是无法预测的。Clock jump 就是说,始终会不断进行同步,而同步回来的时间,是有可能不等于当前时间的,那么系统就会设置当前时间到这个新同步回来的时间。在这种情况下,依赖于物理时间的锁逻辑(比如超时的判断等等)就是不正确的。
  2. Process pause:得到锁的节点,它的运行是有可能被阻塞的。比如 GC,下面这个图说的就是这个情况——client 1 一开始得到锁了,执行过程中有一个超长时间的 pause,这个 pause 导致锁超时并被强制释放,client 2 就得到锁了,之后 client 1 GC 结束,缓过来后恢复执行,它却并没有意识到,它的锁已经被剥夺了,于是 client 1 和 client 2 都得到了锁,对于数据的修改就会发生冲突。
  3. Network delay:其实原理和上面差不多,网络延迟+锁超时被强制剥夺和重分配的逻辑,在特定情况下就是不正确的。

问题可以理解,可是仔细想想这个问题的本质是什么?它的本质其实就是消息延迟+重排序的问题,或者更本质地说,就是分布式系统不同节点保持 consistency 的问题,因为 lock service 和 client 就是不同的节点,lock service 认为之前的锁过期了,并重分配锁给了 client 2,并且 client 2 也是这样认为的,可是 client 1 却不是,它在 GC 之后认为它还持有者锁呢。

如果我们把数据的写操作和锁管理的操作彻底分开,这个问题就很难解决,因为两个节点不可能 “一直” 在通信,在不通信的时间段内,就可能会发生这种理解不一致的情况。但是如果我们把写操作和锁管理以某种方式联系上,那么这个问题还是可以被解决的。简单说,就是物理时钟不可靠,逻辑时钟可以解决这个问题

之后 Martin Kleppmann 提出了解决方案,他的解决方案也就是按照这个思路进行的。他的方法很简单,就是在获取锁的时候,得到一个永远递增的 token(可以被称作 “fencing token”),在执行写操作的时候,必须带上这个 token。如果 storage 看到了比当前 token 更小的 token,那么那个写操作就要被丢弃掉。

Chubby

Chubby 是 Google 的分布式锁系统,论文在这里可以找到,还有这个胶片,对于进一步理解论文很有帮助。从时间上看,它是比较早的。

Chubby 被设计成用于粗粒度的(coarse-grained)锁需求,而非细粒度(fine-grained,比如几秒钟以内的)的锁需求。对于这样一个系统,文中开始就提到 consistency 和 availablity 重要性要大过 performance,后面再次提到首要目标包括 reliability,能够应对较多数量的 clients,和易于理解的语义,而吞吐量和存储容量被列在了第二位。

Chubby 暴露一个文件系统接口,每一个文件或者文件夹都可以视作一个读写锁,文件系统和 Unix 的设计思路一致,包括命名、权限等等的设计都是基于它。这是一个很有意思的设计。

对于一致性的达成,它使用 Paxos,客户端寻找 master 和写数据都使用 quorum 的机制,保证写的时候大部分节点成功,而读的时候则是至少成功读取大部分节点(R+W>N,这个思路最早我记得是 Dynamo 的论文里面有写);如果 lock 有了变化,它还提供有通知机制,因为 poll 的成本太高。

内部实现上面,每一个 Chubby 的 cell 都是由作为 replica 的 5 个服务节点组成,它们使用 Paxos 来选举 master 和达成一致,读写都只在 master 上进行(这个看起来还是挺奢侈的,一个干活,四个看戏)。如果 master 挂掉了,在 master lease 过了以后,会重新选举。Client 根据 DNS 的解析,会访问到该 cell 里面的某一个节点,它不一定是 master,但是它会告知谁是 master。

分布式锁里面比较难处理的问题不是失败,而是无响应或者响应慢的超时问题。Chubby 采用一种租约的机制,在租约期内,不会轻易变动当前的 master 节点决定。在响应超时的时期,客户端的策略就是 “不轻举妄动”,耐心等待一段时间等服务端恢复,再不行才宣告失败:

这个图的大致意思是,第一次租约 C1 续订没有问题;第二次租约续订 C2 了之后,原来的 master 挂了,心跳请求无响应,这种情况客户端不清楚服务端的状况,就比较难处理,于是它只能暂时先阻塞所有的操作,等到 C2 过期了之后,有一个 grace period;接着再 grace period 之内,新的 master 被选举出来了,心跳就恢复了,之后租约续订的 C3 顺利进行。

这显然是一个异常情形,但是一旦这种情况发生,系统是被 block 住的,会有非常大的延迟问题。思考一下,这种情况其实就是从原来的 master 到新的 master 转换的选举和交接期间,锁服务是 “暂停” 的。再进一步,这个事情的本质,其实就是在分布式系统中,CAP 原理告诉我们,为了保证 Consistency 和 Partition Tolerance,这里的情形下牺牲掉了 Availability;同时,为了保证 consistency,很难去兼顾 performance(latency 和 throughput)。

此外,有一个有点反直觉的设计是,Chubby 客户端是设计有缓存的。通常来讲,我们设计一个锁机制,第一印象就是使用缓存会带来复杂性,因为缓存会带来一致性的难题。不过它的解决办法是,使用租约。在租约期内,服务端的锁数据不可以被修改,如果要修改,那么就要同步阻塞操作通知所有的客户端,以让缓存全部失效(如果联系不上客户端那就要等过期了)。很多分布式系统都是采用 poll 的方案——一堆 client 去 poll 一个核心服务(资源),但是 Chubby 彻底反过来了,其中一个原因也是低 throughput 的考虑,毕竟只有一个 master 在干活。

对于前面提到的 Martin Kleppmann 谈到的那个问题,Chubby 给了两个解决方法:

  1. 一个是锁延迟,就是说,如果一切正常,那么持有锁的客户端在释放掉锁之后,另外的客户端可以立即获取锁。但是如果出现超时等等异常,这个锁必须被空置一段时间才可以被分配。这个方法可以降低这个问题出现的概率,但是不能彻底规避问题。
  2. 第二个就是使用序列号了,对于写入操作来说,如果请求携带的序列号要小于前一次写入的序列号,那就丢弃请求,返回失败。

回过头思考 Chubby 的实现机制,我觉得有这样几个启发:

  1. 不要相信任何 “人”(节点),必须询问到多数人(quorum),多数人的结论才可以认为是正确的。这个方式发生在很多操作上,比如寻找 master,比如选举 master,比如写数据。
  2. 超时是很难处理的,它采用了租约的机制保证节点丢失时间的上限,使用 grace period 来容忍 master 选举发生的时延,使用序列号来保证正确性。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

我裸辞了

2024年9月16日 11:10

我裸辞了。

工作差不多十六年了,从来没有以离职后休假的方式休息过。今年还是比较特别的,我做了很多新的尝试,想改变一下自己,包括这最近发生的一件事情。事情发生得很快,我辞职了,在作为 engineer 加入 Doordash 一年零十个月后。

记录一下。

原因

我不是在二十岁的年纪,做决定容易缺乏思考,其实,我已经想了这件事情好久了。在这期间,我也和不同的朋友和同事讨论过,他们有的还在 Doordash,而有的也已经离开了。说起来,大致有三个原因:

第一个,是兴趣的不匹配。

郭德纲说过,如果你每天做的事情是你喜欢做的,那就是老天爷赏饭吃。这样的情况只在少数人身上发生,而我大致就是这样的少数人——不能说每天如此,但是在我职业生涯八成以上的时间,我工作做的事情,恰恰就是我喜欢做的。不过,今年我从一个做平台的 Gateway 组换到了一个做产品的 Order 组,我察觉到情况有了变化,这里面原因有些复杂,但明确的是,impact 是有,但做这个产品工程师并不是我所喜欢的。回头想起来,过往一直都是一个 platform engineer,这算是我第一次做 product engineer,也许这个角色并不适合我。

兴趣,其实是一个复杂的事情。但是匹配还是不匹配,却是可以直接感受到的。有兴趣的时候,我会感觉充满热情,也不太容易感觉疲倦,做事情都很有动力,工作就是一件快乐的事情;没有兴趣的时候,依然会努力做好工作,但这些特点都不在了。

第二个,是对于职业生涯有了进一步的思考。

我想停下来,休息一下,整理一下,总结自己的经验、技能,想一想之后应该做些什么。一方面是作为 individual contributor,我觉得在进一步发展上,我遇到了瓶颈;另一方面,则是看到当前的工作内容和和团队文化,对我进一步在职业生涯的道路上进一步前进不利。

今年有点特殊,我换了一个 team,最近几个月,我在做一个也许工作两三年的 engineer 也可以做的事情,重要程度很高,可是我自己擅长的技能和经验,却没有足够的施展空间。我注意到,这个团队需要解决很多知识迷局的问题,这些问题涵盖了大量的复杂逻辑和业务流程,这些都不是困难的技术问题,而是说,每个新来人都需要较长时间的 onboarding,我倒是不排斥这个,但这种情况下,我的背景就没有太大优势了。我和我的老板也聊过,没有任何责怪的意思,但是我能看得到的是,和报酬无关,和 impact 无关,但和自我价值的实现相关。

最后一个,则是大多数人离开的时候,说的 work life balance。

我不是一个能把生活和工作分得很开的人,我看到自己的生活被工作侵蚀得太严重,尤其是当我看到团队中的 role model,每天长时间在线,被迫忽视所谓的下班时间,一直响应各种问题,你可以说这就是 “卷”,但无论如何,我觉得这对我来说不是一个可持续发展的方向。

事实上,这是一个团队整体的情况,而不是个人的情况。我注意到,无论是讨论、询问,还是争辩,在这里大家都似乎太忙了,无论是谁抛出一个问题,因为太忙了,大家都更倾向于专注于自己的事情,而不是热心地解答。对于 incident handling,经常要面对长时间的加班和压抑的氛围。一张一弛,文武之道,可以忙一阵、闲一阵。如果一直非常忙,这就一定是有问题的。

而忙碌给我带来的影响并不是只有时间上的。其实,每天晚上我也能挤出时间来做一些自己列表上的事情,但问题在于,白天高强度的工作把我的精力消耗光了,不但是咖啡当水喝,有时候甚至连午饭也没有机会吃。这种情况下,到了下班以后,大脑就不想运转了,只想做一些简单的劳动,比如刷短视频,看短新闻,根本不想做其它消耗脑力的事情,比如看书。这些与我的来说,是非常不健康的。

这些事情,更和公司的文化相关。我在离开前,也评估了换一个组而非离职的可能性。但是对于这一点,其实并不能很好地解决。于是我想,既然这个方向与我而言是有明确问题的,那为什么不换一个公司,人生那么短暂,职业生涯那么短暂,没有必要在这个地方吊着。

于是,上周五是我的最后一天。

计划

那接下去的计划呢?

第一步,我打算休息几周,把某些身体健康问题处理一下,做一做体检,解决幽门螺旋杆菌感染的问题,解决牙齿的问题等等;看几本书,基本都是之前找借口没时间看的书;多陪陪家人,和孩子多玩一玩,也找机会出去溜达溜达;放松一下,和心理医生多聊聊,采取措施去努力纠正心理的健康问题;规划适合自己的生活方式,目标能够平衡生活、运动、休息、学习和工作;再把职业生涯的下一步想清楚,而现在,对此我只有个模糊的概念。

第二步,接着几周,则是回顾和总结我所具备的软硬技能。每个人都会积累经验,不过一定时间以后需要总结一下,否则很多经验只是茶壶里的饺子倒不出来,有个朦胧的回忆,但无法在实际中快速落地变成好的想法和观点。近期,我也会多写一些 blog,弥补之前错过太多的遗憾,以技术文章为主。

第三步,才是 job hunting。现在我有公司、团队和职位类型等等大致的目标,但是快到那时,可能会有不一样的想法,但我一定还是 IC。通常来说,到年底工作机会往往不太多,所以我做了心理建设,预期这有可能会花费较长时间。

所以现阶段,我根本不想考虑任何和过往工作有关的事情。

思考

没有遗憾么?

我总体来说确实是个患得患失的人,但我一直想改变自己,并且今年也做出了不少改变,至少我觉得现在更能拿得起放得下。遗憾也许会有,但是这不会影响我当前做决定。我在短期内会失去还挺不错的收入,而离开的 Doordash 其实依然是一家我看好的高速成长的企业,我会继续拿着少量它的股票,并且,现在的就业市场也说不上好。

尤其是就业市场这一条,我想,软件工程师这个职业,因为加息、AI 等等原因,应该比较难回到疫情初期 2020 年那种状态了,那个企业时候借贷成本低,大幅扩招,市场有些疯狂,小红书上一堆其它专业 “转码上岸” 的程序员。市场经济就是如此,但是,长期来看这依然是个朝阳行业。据我了解,现在的就业市场比去年有所改善,但可以说依然不太好,不过,事实就是没有什么是完美的,追求完美也容易走入难以行动的误区,反而做出更糟的选择。

做出决定前思考足够深入吗?

这是一个很难回答的问题,一方面很难评估是不是已经做了足够的思考;另一方面,敢于做决定也是我努力在提高的一件事情,尤其要避免对于完美的追求,不要惧怕失败,追求完美只会让自己失去机会。

不过,可以确认的是,这件事情我思考很久了,今年大概三、四月份就萌发了这样的念头,所以,这不是一个仓促的决定。我和朋友同事聊过不少,包括那些在今年离开的,他们都有很有意思的近况,有找到新的、更理想的工作,有依然在旅行和休息的,还有已经启动创业项目了的。我得到了很多好的点子,这让我对于下一份工作之前,有了更多的憧憬。

关于 Doordash 这家公司,我还是长期保持正面观点的。外卖平台和配送的行业,虽说在我看来肯定谈不上是一个好生意,因为它的门槛低,护城河不硬核,可回想在美的十年间,多少外卖平台零零散散地萌芽、成长、衰落,连巨头亚马逊都尝试过,但是最终也是止损放弃,但是从未有一家公司做这样的生意,能像 Doordash 一样能够做到这样出色的营收和市占率。有的公司是做好生意,但是管理层离谱;有的则是生意难做,但是管理层的决策总是很靠谱,Doordash 就是后者。

感受

在离职前两周,我请了一周的假和家人去了趟夏威夷,接着就是离职前一周比较轻的工作,再到上周五正式离开之后,到今天,已经在那之后休息了接近一周。这段时间我直观的感受是反差鲜明的,我觉得自己放松、从容多了,这更加佐证了一件事情,那就是,无论自己曾经怎么认为,以往工作就是带来了较大的压力,足以明显影响生活的压力。

对于就业市场,虽然我还远没有开始找工作,但是根据被 recruitor“骚扰” 的频率来看,尤其是从小公司雇主来看,现在比 2022 年中下旬我上一次换工作要好了很多,在如今宏观经济的这个阶段,利率高企,消费减弱,没有办法去期望和最疯狂的疫情初期相比,但是已经要比前两年好不少。

前面已经谈到,今年我在努力改变自己,在离职以后,虽然只有一周,但是已经开始看到一些不错的迹象,比如每天的精力比以前好多了,白天时间充满了能量,不再需要那么多咖啡。还有一些事情,在我的列表上,一件一件去做,继续观察能不能让自己变得更好。

我喜欢一个说法,大致是说,有两种人生追求,第一种是 “简历美德”,第二种是 “悼词美德”。第一种是可以写在简历上的,自己的事业成就等等便是如此;而第二种,是在追悼会上说的,都是人的品行和为人等等。我觉得人的一生,就是一个把重心从对追求简历美德逐步过渡到悼词美德的过程。在我当前这个阶段,我觉得工作依然是生活的重心之一,但是未来它的比例应该下降,需要考虑多一点为人处世等其它方面,双修自己的美德。

就记录这么多吧。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

谈谈拼多多和品牌

2024年2月20日 04:06

最近有些思考,想在这里随便聊一下拼多多和品牌的话题,记录一下。

我不是学经济的,也不是学商业的,但是关于品牌和商业模式的话题,很多人都有自己的思考。我想聊聊这个事情是由最近拼多多市值超过阿里的事情引起的。

首先一点,电子商务的红利期早就过去,现在看来,电子商务真的算不得什么好生意,也很难谈什么扎实的护城河。电子商务,最多就是套着已经黯淡马甲的零售业,没有什么本质区别。

也许在十五年前,电子商务是能给人无限遐想空间的商业模式。阿里巴巴这些年可以说把一副好牌打坏,电商成功了,但不能躺在功劳簿上,于是它尝试了大量的 “第二曲线”,可是基本上输多胜少,臃肿的人员不说,投资也是一塌糊涂。阿里巴巴有护城河吗?当然!但是它最核心的零售业,护城河却是被高估的,有人说它现在是处于 “大而不能倒” 的状态。

我想起自己家乡的大型超市,基本上都是每隔几年就有一个新的冒出来,然后就是人头攒动,再接着就是几年后倒闭,几十年了,仿佛就是跳不出这个循环。灯火辉煌的时候人们永远都不会去想象它几年后落魄的样子,但是市场的变化往往比人们想象要快。

曾经有人争论重物流和轻物流带来的区别,可是看看京东,一样在过苦日子。电子商务就真的是一个艰难无比的行业,随着抖音、拼多多这样的越来越多的玩家进驻,伴随经济因素导致的消费降级,很难有人独善其身。

再来看拼多多。拼多多从社交的夹缝中成长起来,在大家都爱 “价廉物美” 的东西,在没法两全的时候坚决地倒向 “价廉” 一边,立足于低消费人群,忽略品牌,生产厂家直销,用病毒式的扩散方式(比如社交平台 “砍一刀”)大量获客,抢夺份额。另一方面,欺压小商家,压低利润率,发布各种保护买家的政策(比如 “仅退款”)。在我看来,它正在做的其中一件事情,就是消灭本土品牌,这一点和以往的互联网电商是背道而驰的。

但是这一点真的很厉害。你会记得你在拼多多、Temu 上买了什么便宜到惊掉下巴的东西,但是你会记得它是哪个著名品牌的正品吗?这就比较少了。山寨货、劣质品、盗版书……在低价的诱惑下,似乎一切都变得可以理解。

我觉得拼多多可能会霸占更大的电子商务市场份额,但是这样的公司,起来得快,倒下得也快。我在北美,Temu 之前,其实已经有 Shein 和 Wish 了,这样的模式其实并不能算独创,我也并不觉得这些主打低价的公司能长久地生存下去。这里面有一个关于护城河的问题,就是 “其它电商能学拼多多吗?”,从渠道、营销,到获客,我认为其实这里的门槛并不太高,也许阿里巴巴能学,也许京东也能学。

我觉得,一个让普罗大众受惠的长期的好生意能做到两件事情,一个是平台,一个是品牌,二者至少具其一。平台指的是可以让更多的玩家加入进来一起玩赚钱的游戏,品牌指的是让消费者能够溢价去消费的口碑。这个品牌可以是自己就成为那个唯一,也可以是帮助其它更多的品牌壮大。

Steam 游戏就是平台,小的游戏发行商都愿意在这里发布游戏、和玩家互动;茅台就是品牌,买茅台的人本身就认可了它较高的价格。苹果是平台,也是品牌。它有一套完整的闭源生态,帮助其它开发者在这里赚钱,这就是平台;它自己也是品牌,我想大家都无可否认买苹果的产品就在一定程度上是溢价的,苹果自己多久代表了品质和用户体验。

拼多多是一个有争议的平台,但是它有流量,而电商盈利最重要的就是流量。至于品牌,我其实想说的是,从国家的角度,我觉得拼多多带来的最大潜在危害,就是在于这个平台对于品牌的扼杀。国产品牌需要在消费者心智中慢慢培养,而从拼多多购买东西,就恰好反过来,一种是没有品牌,一种则是反向品牌的心智培养——成为低价、劣质、平庸的代名词

我想澄清的是,我觉得拼多多是一家好公司,虽然我不喜欢它的生意,但这并不妨碍我认可它的生意也许还会继续壮大。我倒是真心希望中国能有更多的品牌和平台站在世界的舞台上,不是通过廉价和供应链的方式。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

关于软件行业未来和职业生涯的一点思考

2024年1月16日 13:46

关于行业未来

软件行业裁员到处是风声鹤唳,关于行业,关于职业,关于未来,平日里总有一些零散的想法,可是总体来说,始终是忙碌太多,而思考太少。于是,这个周末静下心来,想努力把这些碎片串起来——可是似乎这并不是一件很容易做的事情,那就先把这些想法记录下来。

首先,关于软件行业。很显然的是,任何行业都有周期,软件行业也不例外,软件行业已经过了最疯狂的时间段,现在整个市场都在高利率下,在高昂的借贷成本下冷静下来。我依然记得最疯狂的时候,大概就是在疫情大放水期间,小红书上都有各种行业的牛鬼蛇神 “三个月转码” 的小作文,那些就让我想起了好多年前,我刚读大学的时候,听说招计算机专业的毕业生,因为需求火爆、一将难求而面试大量走过场(那个时候还没有软件专业毕业生)的故事。和做 manager 的朋友聊天,他也说,那段时间招进来的程序员能力整体上很明显下降了一个档次,而对于公司和团队来说,消化这些人的代价又显得很巨大。

软件行业是夕阳产业吗?显然不是。但软件行业是新兴行业吗?说真的,也不太适合谈什么 “新兴” 了。我认为,现在的软件产业依然再走着明确的上坡路,但是市场很明显很理性了。长远看,这是一个太正常不过的、健康的行业发展的过程。就像中国的房市经过了十几年的高增长,需要冷静下来一段时间一样,但是它和房市还不一样,房市的其中一个根本影响因素——人口,在经过了转折点之后,开始走下坡路,因而房市从长远看,缺乏持续上行的根本;而作为典型科技产业的软件业却不是这样,尤其是 SaaS 行业,因此从长远的角度看,这并没有什么问题。

和 2022 年那会的裁员比起来,那个时候更像是一种粗暴和直接的方式去消化疫情期间过度吸纳的人才,而现在的裁员有点像是意味着,这个行业如今已经不太需要那么多的工程师了。现在风口上的概念是 AI,而它再炒作也是和当年的互联网泡沫有所区别的,毕竟那个时候还缺乏扎实的产品和盈利,而现在 AI 已经看得到切切实实能够取代许多(低端)职业的可能性了。因此除去短期的利率变化因素,长远看很有可能就是不需要那么多软件工程师了。

关于职业生涯

一年多以前,我迈出了从大公司走向小公司的一步,也谈不上顺利,但是这 15 个月来,还是有很多收获。很不一样的工程师文化,很不一样的技术栈,很不一样预期和标准,其中的感触是混杂的。

回头看自己过去做过的各种角色,我明显和那些目标明确而技术栈专一的工程师不同,但是领域上既不能算是纯粹的 infra,也非常规的做单个终端用户向的产品,很多都是内部的一系列系统和平台。于是,向未来看,我把自己定位成一个更关注于完整解决方案的 Platform Software Engineer。相应地,有一个概念与之比较的是 Product Software Engineer——我认为这是一种相较于过往的前端软件工程师和后端软件工程师,或者是做用户产品和做基础设施,更加普适的分类方法。

很多人容易觉得 Platform Engineer 更像是以往大家口中的 “后端工程师”,其实这个看法非常不正确,Platform Engineer 也可以做前后端开发,也可以提供全栈的产品,但是最为关键的是,他们的客户群体并非直接的最终产品用户,而是内部的、制作产品的工程师团队们。换言之,他们往往是 “间接” 面相企业的最终用户的——

While Product Engineers focus on building and enhancing features that solve end user problems, Platform Engineers focus on the infrastructure that supports the product.

以上来自 《Product and Platform Engineers》这篇文章,我觉得大致上归纳得挺不错的。

至于为什么我对于这个角色感兴趣,主要还是基于我过往积累的领域,以及我感兴趣的部分。

随着 AI 进一步地融入我们的生活和工作,一方面编程能力越来越普及化,因其入门门槛越来越低;另一方面简单的编程劳动也逐渐被它代替,因此一个不断被拿出来问自己的朴素的问题是——“我的工作会被 AI 取代吗?” 作为软件工程师,唯有保持思考,保持对于技术的敏锐和创造力,我认为这是唯一的出路。如果发现每天开始套用同样的方法去机械地解决问题,去写无聊的样板代码,那它也许就是一个危险的信号了。

再说说近期的计划,我目前在公司内一个做平台的团队中,能学不少东西,做的事情还算有趣,但是我也努力保持敏锐,如果有符合上述方向的好机会我当然不想错过。

就说这些吧,有更多想法的时候,也许我会把这篇再更新一下。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

关于经济和投资,再见,有趣的 2023 年

2023年12月31日 03:04

2022 年末 2023 年初的时候,中国顶尖的投资银行中金公司(中国国际金融股份有限公司)对于中国宏观经济做了十大宏观预测。如今到了年末,回头一看,了不得,居然全错。要知道,能把这十道题全部做错和把十道题全部做对一样,都是只有非同寻常的人才能做到的。

不过,我倒不是想强调中金公司搞宏观分析的团队有多么得不靠谱,而是说,预测宏观本来就是一件无比困难的事——事实上,华尔街的分析师也好不到哪去

股票策略师 Mike Wilson 预测,标准普尔 500 指数即将暴跌。在美国银行,Meghan Swiber 及其团队表示,要为美国国债收益率暴跌做好准备。在高盛,包括 Kamakshya Trivedi 在内的策略师都在谈论中国资产,因为中国经济终于从疫情封锁中复苏。

16 家大行对标普 500 指数到 2023 年的目标点位,预测值从 3675 点到 4500 点不等。可是结果呢,今天是 2021 年的最后一个交易日,标普最终停留位置是 4769.83。换言之,诸位大师们,全错。

所以,我最终想说明的是,我觉得对于任何一个热衷于投资的朋友,都要有着清醒的头脑,就是不要把自己对于宏观经济的判断,太当回事。这些专业人士况且只能把这件事情做得如此不靠谱,你我业余爱好者就更得有自知之明了。

话说 2024 年的中国市场,一票砖家又开始预测了,看看就好,看看就好。对于美股市场,华尔街也一样:

对于今年投资美股的大多数人来说,收益率都应该还挺不错,但是明年什么情况就很不好说了。浮盈不是真正的盈利,正如同浮亏不是真正的亏损一样,明年我将继续坚守着自己的投资信条,在市场里走下去:

  1. 只买少数几个看得懂的公司,多调查,少操作。
  2. 不做短线,不玩期权,只做左侧交易,不设硬止损。
  3. 永远保持高仓位,根据基本面和市场情绪调仓。
  4. 专注个股而非指数,不买两倍、三倍,永远不做空。

关于第一条,我的几乎所有持仓都是科技股,我不怕它们波动大,我也不在乎它们过于单一。因为我觉得自己大致能理解商业模式、护城河和认定能够长期赚钱的公司实在不多,它们基本都是科技公司,而我只想涉足认知范围以内的标的。

关于第二条,我是坚决不做短线的,每一只标的一旦开仓了,就打算拿至少几年。我知道大多数人更愿意做右侧交易(包括趋势交易),而右侧交易往往就必须带有硬止损,这和我的方式是大相径庭的。不过我觉得这无所谓,也没有什么错与对之分。

关于第三条市场情绪,我比较喜欢实用市场宽度、VIX 和 PUT/CALL 来帮助判断。

目前持仓市值上美股和回港中概股的比例大概是略小于 9:1,今年较大幅度加仓了中概的巨头,目前最大的的持仓是 AMD 和 GOOG。今年开仓了 U 和 PYPL,今年关仓了 GBTC 和 MU。

考虑到降息背景和被抱团的美股七巨头,明年比较看好现金流充裕的小市值美股成长股;另一方面,今年下半年,特别是年末,我在雪球上感受到市场情绪已经非常消极了,外资也跑得差不多了,因此我也看好处于价值洼地的中概股。

可是话说回来,中概目前兴许是被低估了,但是考虑到中国目前面临通缩的危机,市场信心的低迷并不是一根大阳线就可以扭转的——考虑到前车之鉴日本 “失去的三十年”,通缩是一个通常来讲比通胀可怕得多的怪物,经济持续低迷,消费信心不足,投资意愿不足,就业机会消失,资产价格持续下跌……这是一个恶性循环,希望中国不要步这个后尘。

通缩的方便说的和不方便说的原因有很多,但其中一个原因是人口,人口中的老龄化问题是一个全球性的问题,但是从下面这张人口年龄结构分布图能够看出,中国的老龄化问题形势非常严峻。这张图是 2020 年的数据,30-60 岁之间人口,正值创造经济价值的壮年,可是这些人老了怎么办,年轻人口的比例已经严重不足。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

音频:我不是码农,我是程序员,我和你一样,为理想而奋斗

2023年9月10日 12:09

不慌不 MANG 播客的主播 FANG FANG 聊了一下程序员的话题,音频放在这里。下面是 Spotify苹果 Podcast 的嵌入代码,国内的小宇宙链接在此。

Show notes:

  • 02:36 “码农” 是不经意的自嘲还是妄自菲薄?
  • 04:45 十年前写下《致那些自嘲码农的苦逼程序员》文章的机缘
  • 11:32 要意识到自己所讲述的故事 narrative 对自己的暗示, “自我验证预言” 与现实的交互影响
  • 15:38 误解之一: 程序员是无趣的人?
  • 17:23 误解之二: 程序员的工作单调乏味?
  • 24:17 程序员也需要全面的能力, 写程序只是工作中的一部分
  • 30:08《你不适合做程序员》? 做程序员的必备特质
  • 34:10 在转码的潮流背景下, 这些程序员的必备特质是自带的天赋还是后天可以培养的?
  • 37:56 “程序员需要独立解决问题,程序员解决问题不是民主选举。世界上绝大部分优秀的系统的绝大部分,总是由很少的人设计出来的” 那么作为普通程序员, 可以享受到什么样的乐趣?
  • 40:33 如何看待一辈子只做程序员? 这是一种无奈的被动选项吗?还是也可以是一种认清楚自己喜好之后的主动选择?
  • 41:10 误解之三: 程序员是一份 “不体面” 的工作?
  • 44:01 匠人 🆚 程序员
  • 45:51 程序员科学的思考方式也让我在生活中解决问题的时候大大受益
  • 48:38 喜欢工程师文化中的平等交流,就事论事
  • 54:20 需要意识到的程序员思维定势(陷阱): 生活中没有程序里 100% 的因果和确定, 以及非黑即白的 0 与 1
  • 57:30 四火的书籍推荐《大败局》

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

关于时间管理的一点新的感悟

2023年8月27日 13:28

从读书,到工作,都离不开时间管理,我相信只要不是属于无比随性的少数人,应该都有自己的体会。因为这件事情太重要了,它贯穿于每日的生活和工作之中。好久以前就写过一点关于时间管理的体会,后来又补充了一些,现在重新开一篇短小的文字,记录一点新的感悟。

变化

随着年岁的增长,我却越来越感到时间管理这件事情在不断地变得更加重要,因为整体的事务数量和复杂度都提升了。

关于其中的原因,我仔细思考过。大致上,在年纪轻一点的时候,兴许每天需要筹划实施五件事,但是现在呢,每天需要筹划实施十件事。相应地,年轻的时候,兴许可能这五件事里面,最终能完成三件;而现在,这十件事里面,也最多能完成五件,看似完成的更多了,却留下了更多的待办事项。

那接着的问题就变成了,为什么现在的事情更多?

一方面是因为年纪增长,本身就需要承担更多的责任,比如需要处理孩子的事情,家人的事情等等,而这样的事情简直太多了。读书的时候,虽然忙,但是事情的类型相对单纯,而现在的忙,则是掺杂了生活、家庭、工作、学习等等各种各样类型的事情。我记得有一句话说,“成年人的世界里没有童话”,繁忙就是常态。

至于另一方面,则是因为科学技术的发展,尤其是信息技术的发展,让一切都变得更可触及和可获得,因此我们的选择更多了,有精力去同时参与更多的事情。比方说,各种社交软件,就会不断打断你的进程,侵蚀你的时间。也许在十五年前,我只需要处理回复短信就可以了,现在我需要处理邮件、短信、微博、微信、Twitter……当然,作为一个社会人,这些也都是有各自的必要性的,我享受于置身其中,并不想成为那种抛弃世俗联络的人。

除去上述原因外,我觉得还有一个有趣的因素,我越来越留意到,人的大脑总是更愿意和更擅长完成单纯的、排他性的活动。因此如果缺乏时间管理,很可能没有头绪,当即可以做的事情有一堆,自然而然产生畏难情绪,即便勉强做起来,也很可能瞻前顾后,很难专注。

时间管理的一大目标就是把当前要做的事情清晰化,可能有十件事情要做,但是根据规划,当前只要做其中的两件就好了,这就让大脑觉得舒服得多,压力也更少。因此时间管理就是变得越来越重要。

策略

于是,有些时间管理的方式方法就有了更重要的地位。

比如说,安排优先级就是其中之一,记得很早以前看过一张四象限的图:重要+紧急,重要+不紧急,不重要+紧急,以及不重要+不紧急。有些事情就需要有更高的优先级,去立马完成;有些事情在当前可以拖一拖,但是随着时间的流逝,它会变得越来越重要。而有些事情则始终没有那么重要。

再比如说,碎片化的时间,碎片化是时间管理的大敌,无论怎么安排,碎片化的时间就是很难做到高效利用,这是事实。因为当时间变得碎片,每一个碎片都需要使用大脑上下文切换的时间,这就降低了整体的实际时间利用率;更不要说在时间碎片中,我们往往具备软硬条件的各种限制,很难实施一些需要大块时间才能够做的事情。

举个例子,在开车的时候,大脑往往具备一定空闲的份额,而这个份额可以用来接受适度的信息,我觉得听播客、听访谈就是一个不错的方式,我算是个喜马拉雅长期的用户。但是这种时间并不适合需要大量思考的行为,毕竟心不在焉着驾驶还是非常危险的。

接着我想说的,是明确自己的能力范围,抓住主线事务。

我以前犯过的错误之一,就是 “野心太大”。这既包括想做很多方面的事情,最终可能很多都浅尝辄止;也包括在想把一件事情做到的程度上,过于激进,导致花了大量的时间,进度却很不令人满意。无论是哪一种,都和没有对于自己的能力准确识别有关。

关于主线事务聚焦,其中一条重要的原则就是不要有太多并行的进程。具体说,每天都不要做太多的不同主要的事情,尤其是这些事情属于同一类型的时候,哪怕这些事情看起来可以在一天内完成。一般来说,需要耗费时间精力的事情,一天做个三、四件就已经是极限了。太多的事情会让完成的效率降低,或者会导致一些低级错误。

举例来说,有时候和同事进行线上的 1 on 1 对话,可能讨论技术话题,可能讨论业务话题,可能讨论职业方面的内容……无论哪种,我的经验是,这样的活动,在每天不宜安排过多。就算它们中每一个都只可能占据半个小时的时间,但是一天的对话超过 3 个,哪怕加起来它们的总时长也哪怕只有两个小时,我发现大脑也会只保留其中的两三个,而自动模糊剩余的部分(换言之,只有最多两三个对话会留下足够深刻的印象)。类似的情况在做很多其它事情的时候出现,比如面试,比如参加类似的技术讨论会。

究其原因,我觉得有两点。一点是很多活动看似只有一个特定的时间长度,但是却有一定的长尾效应。比如一个技术问题可能只讨论了半个小时,但是在这之后,大脑依然会时不时地想到它,并进行进一步的思考。这种有趣的现象其实很有用,有很多问题都是以这种方式想出来的。第二点是人脑具备一种特殊的排他性,这种排他性让自己在类似的情况短期内(尤其是一天)出现几次的时候,只有印象最深的两三个能被比较好地记住。

最后,任务切分也是一个重要步骤。

前面已经提到,时间管理的一大目标就是把当前要做的事情清晰化。因此对于一个模糊而复杂的事务,其中一个重要的步骤就是把它切分,切分成若干个可以完成的部分,这样去处理每一个部分都显得清晰而游刃有余。这有点像软件世界里面的项目管理,任务切分,我想,本质上是相通的。

举个例子,前一阵子需要给孩子申请某一证件,这就需要若干材料,这些材料需要跑不同的地方(譬如要开证明,要在网上递交申请,要写邮件去获取文字材料等等),走不同的流程完成,而这些流程之间还往往存在依赖关系。这就可以把整个过程列出步骤 1、2、3、4 在笔记上,每次只从可以进行的步骤中选择当前能做的,之后等待流程完成以触发下一个流程,同时还需要订立提醒,以避免某个流程因为某些原因超时了,需要特殊的干预过程。这整个过程看起来,其实和一个工作流系统的设计非常相似,每次只关心一个工作环节,这一点也是非常有意思的。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

闲聊投资:亲自体验和护城河

2023年7月31日 12:41

闲聊一点美股投资。以前讲过一点我投资美股的原则(有些想法有了变化),今天想接着再补充说一些。

上周五的时候,和同事闲聊聊到了投资,他提到,巴菲特建议大多数人只需要买指数就好了,因为投资个股对于大多数人来说都是一件长期收益小于投资指数的事情。即便不买指数,还有那么多基金,都很方便购买——事实上,市场上大部分的资金都来自于专业机构,他们有高级的工具,有专业的人员,市场在某种程度上就是一个零和游戏,那凭什么自己做的投资决策能击败他们?如果不能,那为还不如就买相应的基金就好了。我很赞同这样的看法,可是,投资个股也有好处,其中之一就是有乐趣,可以保持自己对于经济、公司等等的敏感性,让自己持续学习。而且,如果寻找到合适的方法,散户有自己的优势,比如散户资金量小,进出非常容易,而且散户有自己的视角,而这方面如果做得好的话,也是可以获得很有竞争力的收益的。

亲自体验

谈及 “自己的视角”,那就必须要保证自己对于投资标的有着一定的认识。而要做到这一点,就必须做到 “亲自体验”,就是说,只考虑自己看得懂的,体验过的。

有很多原则和方法都在过往不断更新,比如估值,但是这一条却没有改变。举例来说,过往两年我还是陆陆续续加仓了特斯拉(当然因为一直都很贵,就没敢买太多),而我本身就是特斯拉车的用户,所以我对于它有着直接的体验,虽说并没有机构那么专业,但是自然有着自己的认识。我知道特斯拉的车内饰质量做得很差(特斯拉为了省成本真是竭尽全力),我也知道特斯拉单踏板在习惯以后使用体验不错。总之,这些林林总总的,我知道哪些好,哪些不好。

再举个例子,Uber,我不算重度用户,但也时不时坐个 Uber,也坐过 Lyft。因此我知道整个 Uber 使用的体验是怎么样的。坐车的时候,我经常问司机,生意怎么样?和 Lyft 比,又怎么样?经过这几年疫情的低谷,总体来说,我觉得长期来看,Uber 是一个还不错的生意,虽说困难也确实不少,而 Lyft,其实很难构成足够的竞争。

顺便说一句,我的持仓中基本上都是科技股,其实也是一样的原因,因为我本身就是这个行业的。相应地,我是不会去投资医药、能源等等板块的,原因很简单,我不懂,也很难有足够的亲自体验。

亲自体验并不是一个形式,而是要形成自己的看法。因此“简单用用” 是无法做到这一点的,必须要多次使用,甚至成为重度用户。再者,有可能的话,要借助自己的专业能力。例如说,我是一个软件工程师,那么我往往对于软件,特别是 SaaS 的产品就有自己的见解。反之,有时候会读到那些分析师的文章,会留意到其中的某些内容会有一些比较浅显的错误,而且这其中的一个原因,就是因为他们中的一些人并不真正懂软件。

护城河

再来说护城河(moat),我知道不少人对于护城河都有自己的看法,但是从我的角度来说,护城河真的太重要了,基本上是我过滤投资标的的重要一条原则。

什么是护城河?最早这个词是巴菲特拿来描述企业的,更准确地说,叫做 “经济护城河”。它可以防止竞争对手进入市场的壁垒,以保证公司能持续创造价值。拥有护城河的公司必须能持续获得超过其资本成本的收益,并获得高出竞争对手平均值的经济回报。

这里的逻辑是什么?我觉得作为一般的散户来说,平时很忙,没有太多时间去盯着企业的状况,因此我们就需要尽量挑选优质的企业。怎么能在各种宏观经济状况和竞争中存活下来?护城河就是其中重要的一条。

从我的角度去理解,什么样的商业模式具备较宽的护城河?我觉得可以问这样一个问题,如果有一家有更多资源的公司要模仿做出类似的产品,它能比较容易地做到,能比较容易地争夺下用户吗?如果能够回答 “不”,我觉得这样的商业模式就是具备一定宽度的护城河了。

举例来说,Zoom 就是一家我非常喜欢的公司,我是它的用户,而且好多年了,虽说这期间我换了工作,但是 Zoom 始终是我远程办公必不可少的工具。基本上每个工作日都要用到,因此我可以说是重度用户。我也用过其它的工具,但是 Zoom 是给我带来最佳体验的那一个。几年前我了解到它的时候,它的股价还在天上,就没有考虑。而如今从估值的角度来说,它的价格已经掉到一个非常有吸引力的位置了,我就问了我自己这样的问题,如果其它一个资金更为充裕的公司要模仿一个 Zoom,做得到吗?

事实上,微软早就这样干了,Teams 就是他们和 Zoom 对抗的产品。我仔细想了想,Zoom 有着相当的用户基础,它们的视频、通话质量在网络不佳的情况下也是非常出色的,可是,这足以构成强大的护城河来对抗 Teams 或者其它竞争对手吗?我反复思考之后,觉得答案是否定的。切换一个这样的远程通讯工具的成本其实很小,或者说,用户粘性很小,迁移成本很小;而视频和通话算法的优势也不足以大到显著占据优势,所以,我虽然非常喜欢 Zoom,也觉得它非常好用,但是我依然不会考虑去买这家公司的股票。

通常在选择小公司作为投资标的的时候,我总是非常谨慎,因为一般来说小公司往往抗风险能力低于那些大蓝筹,而护城河基本就是最重要的一个筛选标准。经过这样的筛选,其实留下来的、且自己熟悉其产品的小公司非常少。

同为小公司,举了一个反面的例子,再举一个正面的例子。Unity 是一家 3D 互动内容创作公司,它的核心产品 Unity 是一个游戏引擎。我们先不说他现在盈利能力并不太好(事实上,它现在的商业模式是订阅式的,而非分成式,这种方式非常有助于抢占市场,却不是一个盈利的好方法),但是它的护城河却是比较确定的。它的竞争对手是 Unreal 游戏引擎,但是开发游戏的用户在选择现成游戏引擎的时候,选择并不多,因为一个广泛使用的通用游戏引擎门槛很高,而且要切换游戏引擎,这里的成本也是非常显著的,因此我觉得它的护城河是非常明确的。

有一些公司类型,或者是整个产业,它们的主营生意都是没有清晰护城河的,这一类原则上需要避免。举例来说,比如航空公司。你可以说航空业整体有它的护城河,但是单一的航空公司,可以说是没有的,用户切换到一家新的航班成本非常低;再比如说纯电商公司,原因类似,到最后可能就是比低价,这对于公司长期利润率实在是不利的。

投资非常难——要考虑的东西有很多:生意模式、财务、风险、估值……;投资又非常简单——找到好的公司,等到好的价格,让时间成为朋友。有一些众所周知的公司,它们有着看不到边的护城河,它们也许不是爆发力最强的,但是它们却有非常出色的抗风险能力。因为,有护城河就意味着,它们可以把成本通过提价的方式转嫁到用户身上。在选好标的以后,而我们要做的,就是等一个合适的价格。比如苹果,比如腾讯,比如英伟达,比如阿斯麦。当然,好公司的好价格是很难出现的,有时候等好几年都不一定能等到。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

学习 OpenAPI 的一点记录

2023年6月18日 02:01

我记得在毕业以前,就大致明白这样一件事情,系统之间、模块之间的交互,要确定协议,要定义接口,兜兜转转这些年过去了,我觉得对这件事情认识当然越来越深刻,也说不清其中的程度。最近做的项目中,开始大量地和 OpenAPI 打交道,一方面要最先使用 OpenAPI 来定义接口,让多个其他交互的模块都遵循它来开发,就是 “OpenAPI Driven Development” 的意思,这没啥特别的;但另一方面,系统中还需要把 Protobuf 接口定义转换成 HTTP 接口定义,并实施地使用 swagger-core 来动态创建 OpenAPI Spec,这就比较好玩了。

gRPC 到 HTTP 的协议转换

先来说说这第二件,动态创建 OpenAPI Spec。我们的网关系统需要大量地涉足两种接口,一种是对内调用 gRPC 接口,需要使用 Protobuf 来描述它们;另一种则是对外开放 HTTP 接口,需要使用 OpenAPI 来描述它们。这就牵涉到了两个事情:

  • 一个是需要设计一种机制,允许用户只用某一种描述语言(可以是 Protobuf,也可以是 OpenAPI)做出接口定义,然后这个定义会被自动转述为另一种接口定义语言,从而实现通过最少的配置,将两种接口都定义清楚了。技术实现上,我们使用了 Wire
  • 第二个是对于任何一个从外部到来的 HTTP 请求,这个系统需要根据定义自动把他转成 gRPC 的请求,并调用相应的内部系统,等到得到 gRPC 响应之后,也相应地转成 HTTP 响应并返回给外部。技术实现上,我们使用了 Vert.x

在这个过程中,我也学到了很多有意思的内容。一个是关于协议转换的,必须要完整地了解 Protobuf 是怎样描述一个接口的,而 OpenAPI 又是怎样描述一个接口的,然后才能谈转换,二者在定义上是有一些无法共同覆盖的部分的,这就需要使用某些替代或者扩展机制;再一个是基于 coroutine 或者 reactive 的异步编程(有的子系统基于前者,有的则是后者),以往写的 service 多数都是 blocking 的,适应 non-blocking 的 service 整个思维模式需要做一个转变。这部分体会还是比较深的,后续再写一点理解和总结。

OpenAPI Spec 驱动开发

再来说说这第一件,接口定义来驱动开发,这本来是一个平平无奇的事情,我记得 OpenAPI 的名字还是 Swagger 的时候就在项目中开始大范围地使用了,可是这一次,我才慢慢体会到它的威力远不止此。

使用 OpenAPI spec 来定义接口,不只是确定了所谓的系统和模块之间的合约(其实合约这一点其实使用任何方式来表述接口都可以做到);它还做到了一点,那就是 “标准化”。或者说,写这个合约的语言,叫做 OpenAPI,它是世界通用的语言,用它写出来的合约叫做 OpenAPI spec,大家都能看得懂。

Control Plane 中多模块对 OpenAPI Spec 的依赖

整个系统可以大致分为 Data Plane 和 Control Plane,前者可以说覆盖了从请求抵达、分解、协议转换,到内部接口调用,并将结果再次转换后返回的过程;而后者则是提供一系列机制和工具,去完成定义和控制这个过程所需的接口定义、序列化、持久化、请求校验、接口版本管理等一系列操作。

OpenAPI spec 是 Control Plane 整个系统中最重要的一个依赖项,有了它以后,很多模块都可以完成它相应的任务,无论是开发上,还是这些模块工作上,它们都可以并行。比如说,校验模块可以根据 OpenAPI spec 来校验外来的 HTTP request 和内部转换 gRPC 响应得到的 HTTP response 是否严格符合 spec 的格式;外部的客户端团队可以获取 OpenAPI spec 来自动生成客户端 SDK;接口定义人员可以在完成 protobuf 的定义后立即查看自动生成的 OpenAPI spec 是否符合他的预期等等。

OpenAPI 工具

有了 OpenAPI spec,或者说围绕它,就可以创造一系列的工具,并且这其中的许许多多都可以自动完成。OpenAPI.Tools 就是这样一个汇聚一系列 OpenAPI 工具的网站,而且基本上都是开源的,许多项目里面都可以比较自由地使用。我自己尝试了其中的一些,也实际用到了一些,我把其中比较有用的,记录在这里:

Avantation

可以根据 HAR 文件来生成 OpenAPI spec. HAR 就是 HTTP Archive format,是一种记录浏览器交互数据的 JSON 文件。上面有全部的访问某个网址的记录,包括 http 头、请求、响应和时间等等信息。

比如我们访问 https://reqres.in/api/users?page=2,然后就可以使用 Chrome 的开发者工具来导出 HAR 文件:

安装:

npm install -g avantation

运行:

avantation get_user.har
✔ GET /api/users/2
✔ all taskes completed

接着就可以查看生成的 openapi.yaml 这个 OpenAPI spec 了.

Swagger CLI

这个功能就可多了,比如可以校验 OpenAPI spec 的格式,合并 spec 等等。

npm install -g @apidevtools/swagger-cli

安装好后,跑一下格式校验:

swagger-cli -d validate ./spec.yaml
{
  "command": "validate",
  "file": "./spec.yaml",
  "options": {
    "schema": true,
    "spec": true,
    "format": 2,
    "type": "json",
    "wrap": null,
    "debug": true
  }
}
./spec.yaml is valid

Vacuum

这个就是 OpenAPI 的 linter:

brew install daveshanley/vacuum/vacuum

可以生成各种格式的结果页面,比如:

vacuum html-report ./spec.yaml

OpenAPI-diff

这个东西用来做 backward compatibility 的检查是个不错的选择。

安装就是一个 docker 镜像:

docker pull openapitools/openapi-diff

对比两个 OpenAPI spec 的版本:

docker run --rm -t \
-v ~/Downloads/open_api:/open_api \
openapitools/openapi-diff \
--fail-on-incompatible \
/open_api/spec.yaml \
/open_api/spec-updated.yaml
...
- GET /endpoints
  Parameter:
    - Delete input in query
        API changes broke backward compatibility

Redoc

可以生成精美的接口文档。

安装:

npm i -g redoc-cli

运行:

redoc-cli build ./spec.yaml

OpenAPI Generator

可以根据 OpenAPI spec 生成客户端、服务端 stub、文档等等,非常好用。

安装:

brew install openapi-generator

运行:

openapi-generator generate -i spec.yaml -g kotlin -o output-client

openapi-generator generate -i spec.yaml -g kotlin-spring -o output-server

上面的例子中,第一个生成客户端,第二个生成服务端。其中参数-g 是可以用来指定某一种生成器

Swagger UI

Swagger UI 大概是这些工具里面我用的最早的,可以比较方便地查看 spec 并个根据它构造请求。

安装:

docker pull swaggerapi/swagger-ui

运行:

docker run -p 80:8080 -e
SWAGGER_JSON=/open_api/spec.yaml -v
~/Downloads/open_api:/open_api swaggerapi/swagger-ui

运行起来后就可以通过 http://localhost/访问了。

Restish

Restish 非常好用,我们部署的 service 需要使用命令行(CLI)来访问,以作为 portal 访问的一个功能上更强大的备选方案,可是去写 commandline 和维护挺费劲的,Restish 可以自动生成这样的命令行。

安装:

brew tap danielgtaylor/restish && brew install restish

运行:

restish api configure rest-example
restish rest-example get-endpoint --service abc --name def

上例中,第一行用来打开交互界面做初始化配置,以从远程 URL 读取指定 OpenAPI spec,第二行则是根据它来进行真正的 API call。

TCases

它可以用来自动生成远程 API 的测试代码,也是比较实用的。

./tcases-4.0.1/bin/tcases-api-test -o
./tcases-output/src/test/com/xyz/openapi -u 10000 -m
 2 -l stdout -B uri=https://www.xyz.com/v1/abc ./spec.yaml

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

一些皓哥和我的故事——谨此纪念陈皓先生

2023年5月16日 15:39

好久没动笔了,昨日得知左耳朵耗子陈皓先生突然离世的消息,颇为震惊和难过,也感到非常惋惜。他曾经是我在亚马逊时期团队中的经理,更重要的是,他是对我在软件工程师这条道路上影响最深的几个人之一,我觉得我应该写一点纪念性的文字。

要说和陈皓的联系,大概从十来年前说起,之前也关注酷壳博客,2011 年底的时候,在南京的我我正在找工作,想去北京,我写邮件给他,附上我的简历和我当时还在 ITEye 的 blog,问他招不招人。那时我只是一个工作三年多的程序员,和我想象的不同,陈皓回邮件很快,也没有什么架子,我们来来回回讨论了不少技术问题。之后赶在年前跌跌撞撞过了面试,两月份我就加入了北京亚马逊,在一个把负责商品在欧洲进行跨国配送的研发团队,他当时是我的主管。

于是我离开南京,来到了北京,可是亚马逊业务调整,我们原有的业务线解散,在选择新团队的几个选项中,我们加入了商品的销量预测团队,这也是后来我在亚马逊期间呆了好几年的团队。在北京的时候,我们团队的氛围很好,基本上工作日每天都在中午一起下楼吃饭,时不时地大家一起打台球、楼下瞎扯淡,私下里我们称呼他 “皓哥”。

工作中皓哥努力推行的团队文化非常鲜明,给作为程序员的你充分的信任,然后期望你牵头搞定项目。从不搞 micro management,也没有什么条条框框,只要做好工作,其它方面随性得很。他很注重技术分享,所以基本上每周都有技术茶话会,我们团队的所有人都分享过不同的内容,有些是基础技术,有些是开源框架,还有些则是当时亚马逊内部的系统,这些其实和实际的工作并没有非常直接的大量的联系,但是这是技术日积月累中的一项。除了技术至上,他要求团队中所有的工程师都必须在一定程度上 “全栈”,从需求分析、设计、实现、测试、上线、维护……全程负责。其实这种模式在如今的互联网大厂已经稀松平常,但是对于当时的我来说,还非常新鲜。无论如何,作为一个坚定走技术路线和喜爱技术多样性的程序员来说,我太喜欢这样的氛围了。

皓哥性格非常鲜明,敢于表达爱与恨。他在自己的酷壳网上说,他 “痛恨各种不从研发团队出发,不从团队和项目出发的流程、方法论、咨询师、SQA、流程部门”,而在实际工作中,他也是这样做的。很多人记得他批过 Thoughtworks 的咨询师脱离实际,他批过 TDD 的种种弊端,批过百度的作恶等等不少,但每次你读到他犀利的文字,却又不得不感慨 “终于有人说真话” 了,这并不容易。

(上图拍摄自 2012 年西雅图出差期间,周末爬 Ranier 雪山)

遗憾的是,和皓哥在同一个团队中工作并没有太长时间,大概只有一年,他就决定离开亚马逊,加入阿里巴巴。在离开亚马逊之前,我们聊天的时候,他说他想建立一个程序员的社区,从具体技术到程序员文化都涵盖在内。在这之后,我们有过断断续续的交流,每次都让我觉得有所收获。2014 年出国前和皓哥吃了个饭,他再一次鼓励我多去体验不同的文化和生活;2018 年我换工作的时候,也和他电话长谈了一次,他跟我讲创业的情况,也再次提醒我不忘初心。回想那一段共同工作的时间,以及在那以后,我从皓哥那里受到了很大的熏陶,这让我在后来的职业发展过程中,无论坦途还是逆境,都坚持走技术路线,保持学习和分享,热爱程序员文化(见 “关于” 页面)。我想,这大致是我从其中得到的最大收获吧。

再一次叹息,人生的轨迹就是这样难以预测,如果没有 2011 年那封写给皓哥的邮件,如果没有加入他的团队,我现在又在哪里、在做什么呢?

再一次地,震惊和难过,也感到惋惜。程序员的圈子里,从此少了一个个性如此鲜明的人;程序员文化的色谱中,少了一种色彩。

永别了,皓哥。

R.I.P.

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

入职后一些零散的感受和思考

2022年11月20日 04:54

有些时候自己会有一些想法,但是过于零散,想把它们记录下来,可是又找不到一个清晰脉络可以把它们串起来。入职已经两周了,我还记得快要五年前加入 Oracle 的时候,我也写了一篇类似零散的文字,不过那个时候是在入职一个月左右的时候。

Facebook 的唏嘘

我记得在五年前谈 offer 的时候,Facebook(现在改名叫 Meta 了)也给了 offer,他们的 recruiter 当然是尽力试图说服我加入他们,而不是加入 Oracle。当时有一句话我依然记得,大致是说不只要看当下 compensation 的价值,要考虑到股票的潜力,因为 Facebook 的股票的潜力是 Oracle 所不能比的(北美软件工程师的 offer 往往都包含固定薪水和股票两个部分)。

其实他这话某种意义上也没错,爆发中的世界最大的 SNS 公司,当时 FB 股价 $175,一直被称作市值最被低估的 FANNG 巨头,最高涨到去年比那时候翻倍了都不止,而今年的低谷则比那时是接近腰斩,这一进一出简直太过疯狂。疫情可以说强行憋出一堆泡沫,接着就是把它们一个个刺破,那些不盈利的公司都雪崩了,大科技是最后倒下的,但依然可以看得到它们摔得有多惨。

我其实还是看好 Facebook,只是我不知道这个 “阵痛” 有多长。众所周知 Facebook 历来有一个产品线单一的毛病,几乎所有的利润都来自线上广告业务,抗风险能力弱,护城河又受到 TikTok 的侵蚀,一旦广告业务受到重创,投资者用脚投票,它必然逃不掉崩盘的命运,这也是市场哪怕在如日中天的当时依然只给一个如此之低 PE 的原因。相较而言同样靠广告主打的 Google 就好得多,护城河也稳健得多。

Facebook 在决定开拓第二曲线的时候,且不说元宇宙的概念是否过于超前,技术成熟度是否还不达标,这样本身又一次地 “孤注一掷” 风险就太高了,这是公司决策层的问题,很遗憾让很多基层员工和投资者背锅了。相较而言,我还记得 Amazon 当时也有重大的失败,比如它的手机业务,但是它有好几个项目并行,这其中有的业务就做起来了,比如 Echo,这样结果来看还是要好很多。

公司和团队的选择

我在成熟的团队里面工作过,也在成长期的团队里面工作过,这里面的感受各异,但是和过往模棱两可的偏好不同,现在我对于这两者的选择已经很清晰了。在我会尽量选择刚建立、或是依然在高速扩张期的团队,以 “和团队一起成长”。这其中主要有这样几个原因:

  1. 这样的团队更容易都带有做事和积极的文化,轻流程、少扯皮,关注的是增长;
  2. 这样的团队往往意味着前景和公司资源的倾斜,这对于自己的职业发展也有好处;
  3. 这样的团队在技术上限制和顾虑更少,更容易采纳和推行当时前沿的技术和思想。

在选公司的时候,以往我也不太挑剔,后来我逐渐意识到,公司的文化是一个深入骨髓的东西,选择那些真正的互联网公司、小心地避开那些老旧的文化这有多重要。哪怕一家传统的企业要砸钱转型,企业文化要改变却是极其困难的。

我在加入 Oracle 的时候,在 the first generation 失败以后,专门在西雅图成了 OCI,孤注一掷,从头开始做 the second generation,在西雅图的其中一个目的就是为了从当时的三大云巨头 Amazon、Microsoft 和 Google 砸大钱挖人,同时也想尽量保持 OCI 文化和运作的独立性,这其中的决心和雄心不可谓不大。当时觉得大家做事的氛围和风格还可以,可是后来就逐渐发现,企业文化这东西真不是那么容易改变的,尤其对于这样一艘巨轮来说,逐渐 Oracle 的一些缓慢和笨重的老毛病就慢慢散播开来了。团队之间的扯皮越来越多,一年中的 code freeze 时间占比越来越大,org 中开始有越来越多的 program manager 专门催进度……我觉得自己做得不够果断的是,我确实看到了这些问题,却没有足够警醒和趁早行动(其实也是因为懒……)。

有了一点危机感

工作这么多年了,自认为一直比较努力,但也比较顽固和皮实,属于那种到处瞧不上,各种爱谁谁的那种,因此我也很少有危机感。但是这一次骑驴找马的过程中,我却真真切切产生了明确的危机感,特别是技术方面的 hring bar,高到让我惊讶。于是这似冷水浇头,让我打了个冷战,相当警醒。我当然明白这里面有很多客观经济大环境的因素(最近大的科技公司都在疯狂裁员,Blind 上都说 DoorDash 的某 software engineer 岗位 referral 的 pipeline 都排了一百多个人了),可是我觉得自己在 OCI 的这几年之后,在某些我看重的技术领域方面,有些落后了。

回头想这其中的缘由也不难理解,所谓逆水行舟,不进则退。要做一个软件工程师,坚定地走技术岗,技术能力就需要不断刷新,持续学习是一个必选项。这几年的工作内容中,啰嗦的会议太多,无奈的扯皮太多,冗长的 ops 太多,折腾的流程太多……看似每天都很忙,还得做各种技术和业务的决策,两大会三小会的,其实自己的收获却并不理想。我的角色更像一个复杂的综合体,很难说自己还是一个 IC(Individual Contributor)。

现在我终于可以多做一点一个软件个工程师该做的工作,总体来说显然这比每天扯东扯西更让我让我踏实,技术方面我尤其在努力往回赶,当然,我明白这需要时间。

Onboarding

总体来说,入职 DoorDash 的体验还是很不错的,接近三周的集体线上 onboarding(目前完成了两周),少部分时间业务,大部分时间工具和技术,从后端、前端、数据平台到运维工具,全部覆盖,再加上大量的现场实战课,公司创始人之一和我们这一批新入职的一百多人一起在线搭建环境,这大概是入职后我觉得最不可思议的事情了。

回想在华为,onboarding 是有集体流程,在深圳,住百草园,上大教室课,但那主要是推公司文化,大搞团队建设;在亚马逊,有一些公司文化和流程的课程,入职后的 onboarding 主要还是交给团队自己来做了,接着就是大量的 wiki 阅读;在 Oracle,第一周基本全是在线培训,也是基本不讲技术,讲的主要是流程和法务,第二周开始交给团队自己安排,可见 onboarding 是一个管中窥豹的好途径,从中就能看出一家公司的文化了。

现在我手头有三条学习的线路并进,其一是公司给软件工程师集体 onboarding 的 sessions,其二是团队内部的 onboarding 内容,尤其包括了日常最频繁接触到的工具和技术,其三则是项目的背景业务逻辑。总体来说时间还是比较紧迫的,希望等这些内容熟悉了会好一些。

疫情带来的深远影响

我们团队几个人在多伦多,几个人在湾区,几个人在纽约地区,几个人在泛西雅图地区,如果这件事情发生在 2019 年,那应该算是一件稀罕事。但是疫情改变了太多东西,其中一条就是远程办公。我记得在国内工作那时候写过一点在家办公的想法,之后在疫情刚开始的时候又写了一点(不得不感叹 37Signals 真是太有远见了),而现在呢,整个求职过程,整个离职过程,以及整个入职过程,我没有去一趟公司,这是真正的纯远程办公了。

从科技公司的角度来说,我认为长远看这是好事,省掉了大量的场地开销,人才招聘扩展到更多的区域,终于不用在湾区、纽约和西雅图这样的软件重地抢地盘了,地域优势真在逐渐淡化;从扎实的 IT 从业者的角度来说,这样也是好事,多一点可靠的基本功和硬实力,少一点速成和赶风口的心浮气躁。尤其对于那些行动不便的人来说,所有通勤的烦恼全省了。当然,好事指的是总体来看利大于弊,并非没有弊端,弊端有很多,以前我也谈到几次,每个人也都有自己的理解,这里就不啰嗦了。

因为疫情期间很多公司做了不切实际的预判,过度疯狂地招人,在如今经济进入衰退的时代,也就有不少求职难的问题暴露出来。但经济和市场就是如此,有周期,有波动,衰退的结束就是增长的开始。没有人知道经济下行会到什么时候完结,但是我们可以确定的是,好转的时候总会到来,也许一年后,也许两年后,现在的时间就是蛰伏的时间,机会没那么多,但是好的公司和优秀的人才,总是能做出合理的应对,在特殊的环境中找到对方。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

近况更新:第三次换工作

2022年11月7日 05:00

最近发生了太多的事情,也没有更新 blog,来冒个泡。

我基本上是一个很长时间才会换一次工作的人,说好听点就是爱惜羽毛(工作经历),注重长期积累而非短期回报,说难听点那叫懒得动弹。

记得在 2012 年初的时候,我在人生中第一次换工作,从华为跳到亚马逊,那时候我已经在华为干了三年半了,其中的主要动机一个是想开阔眼界,另一个是可以不那么辛苦。

接着就是 2018 年初,在亚马逊干了六年之后,第二次换工作,从亚马逊跳到甲骨文,主要动机有两个,一个是我想在职业生涯有个突破,去参与云计算的浪潮;第二个是我觉得当时我的薪水已经严重偏离市场能给我的待遇了,所以想去谋取一份更合理的薪水。

这一次,在甲骨文干了 4 年 9 个多月,第三次换工作,下一站是一家相对较小的公司 DoorDash。这次的主要动机有三个,一个还是职业生涯方面的,如今干的事情越来越偏离我认为工程师最应该干的事情,做很多的 process management,很多的协调和扯皮,很多的项目和任务管理,以及很多的 ops,而在技术方面也缺乏足够多有意思的挑战;第二个则是在 compensation 方面遭遇了一个出乎我意料的大 cliff,它让我非常不爽,于是我觉得必须得离开才能得到合理的待遇;第三个,则是客观的经济大环境,决定了现在风险资产都在低位,它就像一个照妖镜,是好公司和好的商业模式,还是风口上的猪,如今都看得清清楚楚,目光放远的话,这就是一个宏观上的 “抄底” 良机。

这一次找工作和五年前不同,客观方面,又是裁员,又是 slowing down 或者 hiring freeze,这次市面上的机会明显少得多,而且我觉得僧多粥少的原因也让 hiring bar 高得多;主观方面,工作很忙,琐事很多,我也就没有那么多时间去准备,面的公司也少了不少。于是,最后我的选择也没有五年前那么多。

我觉得在长期通胀和经济衰退的大背景下,我觉得有两件事情对公司来说无比重要,一个是经济护城河,或者说,将成本转嫁到消费者身上的能力;另一个则是现金流。在两者都满足的情况下,我觉得,小一点的公司更有回弹的潜力,而且,我都在大公司干了快 15 年了,是很想去中小公司体验一把的。于是 DoorDash 在我看来,是一个不错的选择。对于新的团队,我做了一些功课,也和 manager 聊了一些,这个团队是负责 Gateway 的。

对于我在 Oracle(OCI)的故事,之前都还不错,可有点遗憾的是最后的收尾并不那么美。整个过程其实很冗长、很复杂,今年暑期左右我的老板,以及老板的老板都撤了,这件事可以说好多人都是管理层政治斗争的牺牲品,其中的细节我就不透露了,但显而易见的是,对于工程师来说,这样的事情显然是非常不喜欢的。从我自己选择的角度来说,当时没有及时决断并离开,而是抱着走走看看的心态,现在看来是一个不太好的决策。

长远来看,这一次的过程中,我也得到了一些经验,有了一些启发。曾经在几年前,我就考虑过,在接近 15 年工作经验的时候,要改变策略,依然是软件工程师,依然是 Individual Contributor,这点不变,可在有了一定的积累之后,我决定不再是什么都想尝试一下了,而要变得稍微挑剔起来,初步确定下细分的一个方向。

最近这些年来,我都没有去做直接面向互联网一般用户的产品,而是立足于一个有足够影响力的数据平台,偏向于基础设施层面,关注于一个完整的解决方案栈,而非单一的某个具体技术或者具体服务。从这些年积累的经验和熟悉的技术栈来说,我觉得未来我还会继续遵循着这样的思路。

对于现在的大环境,每天都有裁员的新闻产生,我也陆续收到好几个求职方面的求助,对于那些职场上经验不深的工程师朋友们,如果你有能力做到未雨绸缪(注意,这是前提,毕竟只是 “站着说话不腰疼” 就太恶劣了),那么,我的建议就很简单了:第一,时刻准备着被裁,你有余粮,你有技能,你有经验,永远有你的 plan B;第二,经济周期就是这样,好好坏坏,心中不慌,淡定思考,把经济的低谷视作黄金机会。如果第二条觉得自己做不到,那很有可能就是因为第一条没有做到。“此处不留爷,自由留爷处”。

前几天和朋友聊天,朋友问,你这个时间点换工作是逆势而为啊,肉身抄底,可抄到底了吗?把自己赔里面怎么办?我说,这谁知道啊,08 年我毕业那会正好是上一波的低谷,如今算是第二波——过了一年,要大形势还在恶化,咱可以接着抄啊。

当然,我也知道很多朋友出于种种原因,没法做到足够的 “未雨绸缪”,或者已经被裁员波及,比如在北美拿工作签证的工程师朋友们。那么,如果你需要,也可以和我联系(网站的右上方有我的各种联系方式),我很愿意在力所能及范围之内帮助你,即便无法帮到你,咱们也可以聊一聊,也许有些困惑就解开了,最不济,说不定心里也会舒坦一些。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

❌
❌