@Lenciel

在研发流程中使用 RFC

Why

在各种不同阶段不同规模的技术团队里工作时,很容易遇到两个困扰。

一个是技术决策不够透明或者过分集权带来的参与感和信任度的问题:每个代码库的贡献者都应该参与到架构和高层次的技术决策中。每一行代码都是代表自己,同时也代表团队的其他人,做出的决定。因此,随着团队规模增长,技术决策的透明度如果不够高,员工的参与感和彼此间的信任度都会很快下降。

一个是技术决策的可见度不高带来的技术债务问题:由于并不知道其他人在干什么和他们是怎么干的,每个团队都可能在构建一些重复的东西,并且使用了各自不同的方法和技术栈。对于公司,技术团队构建的系统不是资产就是债务,而前面说到的这些无论从内容还是质量上,大部分都是债务。

因此,作为一个团队,需要一种更好的方式来做出和分享技术决策,这里的「更好」的标志是:

  • 让团队成员而不是技术委员会为他们所负责的系统做出决定
  • 允许领域专家在没有直接参与构建特定系统的情况下参与决策
  • 控制做出决定的系统性风险
  • 减少同步上下文的工作量
  • 在未来有一个可回溯的快照
  • 能同时处理多个项目
  • 是异步的

要同时满足这些要求,粗看起来是相当困难的。但幸运的是,软件团队都会遇到这个问题,因此已经有很多方法论。在研究了很多方法特别是开源软件中实际使用的方法之后,我觉得采用 RFC 来驱动研发流程的效果是最好的。

What

RFC 已经有了很久的历史,它的字面意思就是「请来拍砖」。可以说,互联网就是在一个个涵盖它方方面面的概念、协议、流程的 RFC 文档从编写、讨论到定稿的过程中,得以成型的。

这套方法如此有效,以至于美国政府里的很多机构都在使用它。

RFC 进入软件公司时间也不短了。不管是 Facebook、Google 这样的大公司,还是 Uber 这样的独角兽,都有类似的工具和流程

我个人觉得,对于不同规模的技术团队来说,文档应该有多么全面,文档应该面向谁,文档评审的过程应该有什么,都是不同的。但一个比较结构化的形成、记录和分发文档的方法,对各种公司都会有用:把决策写下来,把知识传播出去,并允许所有人评论,这就是 RFC 的价值所在。

把决策写下来

把决策写下来并分享给他人,是更好的思考过程,也带来更多的责任感。

团队希望提高代码质量?进行正式的 code review,把意见和反馈写下来。 团队希望减少开会浪费的时间?会议前发布讨论的议题,会议后发布会议纪要。 团队希望减少信息不同步带来的惊喜(惊吓)?把决策写成 RFC ,并与他人分享。

第三项之所以在很多团队都没有被采用,是因为工程师是讨厌浪费时间的人。和写 code review 和会议纪要比,文档通常被认为是浪费时间的,因为它很枯燥也很没有参与感。

如果每个人都同意每个任务应该如何完成,那么写下来确实有点浪费时间。问题是什么、将要进行哪些更改或者新开发、哪些棘手部分有了怎样的共同理解。团队中的某个人花几个小时写下来,如果其他团队成员在读完就只是竖起大拇指表示赞同,那他不如去写代码实现了。

但通常情况下,没这么容易。我们经常听到的是:

  • 「我们开会的时候,我不是这个意思。」
  • 「这个我漏掉了,得临时加一下。」
  • 「我们发现这里改了,会破坏系统的其他部分。」

所以,把决策写下来是非常必要的。

在组织内传播并得到反馈

写下来可以带来更好的思考和责任感,但 RFC 的真正魔力在于它在组织里面公开地传播并得到反馈。组织中的人传递的信息在很大程度上决定了组织文化的形成。可以向每个人发出自己的提议并邀请任何人发表评论,会为组织定下信任和尊重的基调。

并且,这也是在整个组织中保持一致的工程框架的关键部分:当人们看到自己造的轮子早已存在或者有了更好的解决方案时,他们就会使用。

另外,在整个过程里面,管理者还有义务为大家建设一个心理安全的环境。比如通过支持「新手」的标签,来允许大家在缺乏专业知识、背景或信心的情况下,大胆地发布自己的建议或者 RFC 。

How

我们尝试的流程是:

  1. 在构建新东西之前做好计划。这可以是面对面的白板,也可以只是用 IM 聊天,只要能搞清楚将如何完成任务。
  2. 在一个简短的书面文档中记录这个计划。简短清晰的写清楚 Why、What、How,不要走极端。
  3. 选择少量的人来 review(高级工程师、团队中将使用你要构建的特性的人)。
  4. 根据上一轮 review 修改后,把这份计划发给公司的所有工程师,请大家都来评论它。
  5. 让每个团队对比较复杂的设计都采用上面的流程,然后不断迭代。

「构建新东西」的粒度是什么,或者说,怎么判断「应该为这个事情写一个 RFC 吗」?

我觉得如果你:

  • 构建新的系统、应用、组件、库等等
  • 开始打算重构、重写现有系统
  • 做出会影响不止一个系统或其他团队成员的改变
  • 增加了一个新的依赖或者改变了现有流程
  • 希望定义客户端或系统之间的契约或接口
  • 计划在技术栈里面引入新东西
  • 不知道你是否应该写

那就写一个吧。

逐步的,公司可能会迭代出 RFC 的模板,因为大量的文档会有一些共有的内容结构比如「做这项工作的动机是什么?」或者「怎么做测试?」。

最后,这个流程对各种规模的团队应该都是适用的。 它不仅解决了可见性,减少技术债务,而且还传播知识,让工程师每天更加投入。并且,这是流程的启动成本很低,我建议任何小型或中型的技术团队去做,特别是正处于成长阶段的团队。

使用 Alfred 完成文本扩展

有很多文本片段是可复用的,它们被称为 snippets 。

收快递的地址,手机号,身份证号是一类:格式和内容都固定。

Markdown/Jekyll 里面各种宏是一类,格式不变,内容变化:

{% highlight javascript %}
/* Some pointless Javascript */
var rawr = [“r”, “a”, “w”, “r”];
{% endhighlight %}

再比如会议纪要这类文档,框架也是固定的,日期、参会人、内容等具体内容是变化的。

如果只是把它们放在某个地方,每次用的时候搞出来拷来拷去,显然效率很低。所以市面上有「文本扩展」类的工具专门解决这个问题。

过去我用 TextExpander,它的功能很强大,除开简单的格式,还支持了各种宏。

但是自从它改成按月订阅付费,并且每个月要好几十块钱,我就转到了 mac 自带的「shortcut」:

它的功能很简陋,特别是不支持宏(后面可以看到有它没它区别多大)。

这两天迁移到新机器,偶然发现我掏钱的另一个软件 Alfred(没它我真是不知道怎么活)有了一个 Snippets 功能。

试用了一下,发现基本够用了,如果能做一个导入 TextExpander 库的功能就好了。

拿写 markdown 的文档时经常要插入图片这件事情举个例子。我们可以在 Alfred 里面建一个类别,Prefix 为「.」,keyword 为「mki」,然后它的公式是:

![{clipboard:0}](/downloads/images/{date:yyyy/MM}/{clipboard:0} “Don’t touch me…”)

这里:

  • {clipboard:0}这个宏是取你剪切板堆栈里最靠前的内容,简单说就是你刚刚复制的内容。
  • {data:yyyy_MM}是取当前年月日并格式化为2020_03,因为我有一个 Hazel 脚本,写 Blog 用的图片扔进一个目录,就会被自动按照「年+月」的这个格式归档。

于是,每次要往 Blog 里面插入一个图片的时候,只需要复制文件名,然后在编辑器里面敲击 .mki 就可以了:

Alfred 支持了非常丰富的宏定义,满足工作中的大部分需要足够了。