每次想要说些什么,但在动笔时总是苍白无力。

工作

在疫情的第三年,我们公司走的磕磕绊绊。一方面是疫情的缘故,导致商业化收到影响。由于我们的产品不成熟,云上形态也没有明确,目前还是以线下部署形式居多,并且不少单都是单纯靠堆人拿下合同,变现效率可想而知。再加上去年几波疫情的影响,公司虽然今年的收入有了不小增长,已经完全达到我的预期,当然从投资人和公司高层的角度,这些收入显然是不能完全支持这样一个团队的。

另一方面,作为一个数据库内核的开发,整个产品定位的不清晰,人员和组织架构的变动,以及来自技术和商业化的双重压力,都导致这个产品没有走上捷径。以我在我司的司龄,足足见证了三个大版本的重复造轮子,每一个版本都是号称解决了若干问题,但解决方案都没有经过仔细的评估,连最基本的逻辑自洽很多时候都难以保证。造成这些问题的因素很多,这里只列几点。

  1. deadline。这一部分主要是来自商业化的压力,任何无法落地的技术都没有前途,在一个创业公司更是如此。因此每个版本会有大量需求,其中多数都是从某个或者某些用户而来,或者是友商有的功能我们也得有,最无奈的有时一个合理需求,但因为技术实现和产品架构的原因,导致实现起来困难重重。所有这些需求放到一个不足15人的内核团队,deadline的压力可想而知。要论我司什么时候PR最多,那一定是发版前。部分的PR因为工期来不及,没有进行相应逻辑梳理和抽象,时常出现因为既有代码导致需求无法完全满足,最终只能饮鸩止渴或者敷衍了事。还有因为发版前不断合入PR导致的新问题,这类问题绝不在少数,通常的原因可能是需求不够清晰或者前期准备工作不足,导致开发没有考虑的特别周全,导致最后逻辑无法自洽。
  2. 前人开坑,后人填不上。去年我们开始做第四个大版本,目前暂且还看不出来是不是重复轮子,这其中开坑数量几乎已经达到了惊人的两位数。说的好听是,产品的前途非常光明。但现实是,没有这么多人来填这些坑。一个产品,重要的不是那些花枝招展的fancy feature,而是最核心的那一部分core feature。与此同时,多个版本同时维护,对开发和测试造成了不小的心智负担。随着各个版本的迭代,代码差异也不断增大,公司要求有稳定的LTS版本,又要为以后着想进而开发新版本。理想情况当然是两条腿走路,但实际上是两条腿互相使绊。比如老版本产品稳定性和性能不如人意,导致最终POC时落于下风,但与此同时,所有内核精力都在新版本。如果回过头来进行大量维护,又会导致新版本进度跟不上。
  3. 工程能力始终跟不上。有一说一,内核这边技术氛围还可以。大家也喜欢看一些paper,讨论一些架构,但最后都很难落到代码上。这其中不可否认是前面所描述的人力问题,但工程能力不足的问题也绝对不可忽视。逻辑上没有做好合理抽象,代码组织不够清晰,再加上基本功不够扎实,实现代码就会出现又臭又长的现象。这些工程化上的不成熟,间接导致了其他问题,绝不是一朝一夕可以解决的。在我看来,如果你写的每一行代码,即便是调用三方库的接口,如果没有足够清晰的认识,当出现线上问题时,只能两眼一抹黑。从这个角度说,工程上的问题甚至比其他问题更难以解决。

虽然走的磕磕绊绊,如果我真的对公司失去信心,也就不会思考这些问题了。如果能形成从商务到产品再到技术的正反馈,我想整个公司的运作效率会大大提高。

这么一回想,我虽然是个老实本分的开发,但我的吐槽体质注定导致我的格局始终打不开,没办法带更大的团队(不会画饼)。

技术

再说说和个人相关的吧。工作上,既有版本就是各种bug fix略去不谈,新版本因为要遵循GQL标准,从整个存储设计上都有一些改变。一部分时间是在做底层的数据和索引编码,其中还试着用了一些SIMD指令优化编解码速度。另一部分就是对分布式事务做了比较详细的预研和设计,看了不少数据库的方案以及paper,难度是真的不小,看看后续能否落地以及落地的工程质量吧

今年貌似技术相关的书还是看了一些:The C++ Programming Language算是巩固基础吧,毕竟作为C++之父,能够精确阐述C++很多技术细节,对于一个熟悉C++的人来说,知道是什么可能比一些tips要重要。C++ Templates,因为工作上有需要,算是比较系统的学习了下模板,坦白说已经忘掉了大多数。perfbook除了个别的几章跳过,整体算是扫了一遍。这本书是从并发编程的角度触发,比较详细的说明了几个问题:

  • 为什么并发编程难?
  • 在并发编程的前提下,在系统设计上可上需要做哪些考虑?(比如分片,资源的所有权,多线程之间的同步等等)
  • 有哪些数据结构或者方法能够帮助更好的完成并发编程?(比如锁,RCU,Hazard pointer等等)

其实perfbook的细节也已经记不清,但这本书整体内容非常不错,能够非常深入的去讨论在并发系统中常见的问题,并且书中结合了原理和实现(除了整体代码风格非常不mordern),同时做到了深入和浅出。今年如果有时间准备再翻一遍。

另外一大块是因为分布式事务选型,在过程中,终于算是入门了TLA+(这至少是有印象的第三次了,前两次都中途放弃)。但这次学习TLA+的过程还是充满了正反馈的。入坑的主要流程如下:

  • Practical TLA+这本书,以及作者的教程,这期间主要是熟悉语法。
  • HLC作者的博客Metadata (muratbuffalo.blogspot.com),这里面有好几篇和TLA+相关的博客,在阅读完之后,我能够在抄袭的基础上,能够简单的用TLA+描述几个时钟:logical clock, vector clock, hybrid logical clock,以及一些简单的并发问题比如哲学家问题。
  • 在这个基础上,开始尝试对我们的分布式事务做原型验证。

整个过程异常痛苦,但绝对充满正反馈。这也是为什么我觉得通过TLA+学到了很多reasoning的关键之处,通过TLA+来验证你的系统,你需要能够精确的描述系统的约束条件和工作流程。通过对这个模型的不断调试和检查,你愈发能够更加清晰的知道系统的边界在哪里,哪些precondition是不合理的,哪些设计是违背了系统约束。当你发现这些问题的时候,会不断促使你思考并改进模型,然后进行下一轮的调试。在这个迭代过程中,你不仅能够对你所描述的系统又更加深入的理解,也能帮助你在和其他人沟通的时候更加精确的描述这个系统。

Clarity begets more clarity. Focus begets more focus.

但不得不说,TLA+绝对是我接触过的东西之中,学习曲线最为陡峭的一个,其难点主要在几方面:

  • 缺少高质量的入门教程,不同教程可能又混杂了TLA+和它的简化版PlusCal,两个版本的语法又截然不同,非常容易就迷失在语法的各种报错中,导致始终难以理解其中精要。
  • TLA+的表述方式需要有一定的数学基础,并且它的表述习惯和语法描述对习惯于编程的人来说比较难以上手,这也是为什么我到现在也只会使用更符合编程习惯的PlusCal。但即便是使用PlusCal,它的语法习惯和实现技巧也不是短时间能够掌握的。更不要提它的调试工具,也就是TLA+ toolbox的报错信息非常反人类。
  • 难以致用,这里不是说TLA+的表述能力不够,而是说需要在学习过程中,找到一个合适的对象(可以是某个系统、系统中的组件或者算法),并用TLA+来描述这个对象。其难处在于如何把握描述的度,这其中需要精确的从这个对象中剥离出核心的逻辑和约束条件,否则TLA+的specification会写的就会像一份伪代码,难以验证系统或者算法的正确性。

去年还算是在工作和学习的过程中,把一些成果更新在博客和Notion里面,算上杂七杂八,居然更新了20篇左右。坦白来说,我的技术博客更偏技术细节一些,主要原因在于这些细节如果不记录和持续回顾,就会迅速淡忘。而我自己又不太喜欢更新类似paper reading的博客,主要是因为这需要耗费大量时间来消化和提炼,不如写一些技术细节来的平铺直述。以后可能也会考虑吧最近看过的一些值得回味的paper做一些简述。

生活

去年,我们迎来了家庭的下一代成员。不管是从备孕、孕期还是到现在娃将近9个月,我对我媳妇有了很多新的认知。我这辈子注定无法体会分娩时的痛苦,但当小孩来到这个世界的时候,我想我媳妇肯定是激发了不少过去没有展现的人格。杞人忧天的体质和瞻前顾后的犹豫都没有阻挡她事无巨细的控制欲,比如需要精确到用哪只手哄娃入睡、每天在脑补娃的各种健康问题、以及对阿姨的”PUA“等等。好在除了娃继承了我吃饭睡觉都不怎么行的特质之外,一切都还比较顺利,老母亲在最近开始工作之后,终于有了除了娃之外的关注点。

有了小孩之后,我们的人生都发生了不小的变化。媳妇的注意力几乎全部放到了娃上,几乎每天都在焦虑不同的问题,以至于我一直都跟她说不要忘了你首先是你自己,然后才是小朋友的母亲。至于我,除了内心中默默感激她之外,貌似也没承担更多的家庭责任,内心中有些许的不安。

从我的视角看,人生从有了小孩才算真的开始。这其中道理很简单,大多数人并不记得是如何长大的。我对于我6岁以前的事情,几乎没有任何印象,唯一能够回忆起来的一件事情就是幼儿园老师让第二天带一个硬币(可能是5分钱),第二天我妈把我送到幼儿园之后,我发现我没有带硬币,于是自己走了两公里左右的路到了我妈单位,跟我妈说没带硬币。如此回想的话,我小时候的经历都是笼罩在一片迷雾中,隐约有个轮廓,却又无法看清。

只有当一个人成了父亲或者母亲之后,才能以一个成熟的个体,试图去回忆和窥探自己作为一个婴儿时,是如何从襁褓中被抱出了医院,如何从躺着翻身,试图用四肢力量趴在床上,进而开始像史莱姆一样爬动,接着完成坐立、站立和蹒跚学步的整个过程。每个人在回忆自己过往的时候,几乎所有事情都是以自身为中心,其他人物都只能作为配角出现,就好比我肯定无法清楚描述小时候我的父母对我有多少关爱或者操心。下一代的出现,正好弥补了这一个过程。我借助我的女儿,填补了我作为婴儿时的记忆,这不正是我人生的开始吗。至于我的女儿,也会在她成为母亲时补上她缺失的这一段。如果把每个人的生命画在数轴上,在大多数情况下,相邻的两代人会有一段交集,这段交集或长或短,剩下的那一段,只能靠再往上或者再往下一代去填补了。

生活的其他方面,似乎都因为孩子的出生,不断的在缩减。比如去年竟然没有读几本与技术无关的书,只记得修陪产假时读了一本极度不符合国情的自传小说Educated,以及年末好像恶补了一阵子希腊神话和北欧神话。只有游戏,更是时间大大减少。在娃出生前入手了PS5,只玩了艾尔登法环、大表哥2和只狼就一直在吃灰(不过只狼和大表哥是真的很有意思)。还有每天通勤路上的炉石,竟然被垃圾暴雪宣判了死刑,不想多说。

新的一年,但愿技术上有所进步,生活上可以多陪陪家人,心灵上有所触动,这就足够。