从Google的单代码库模式看Google工程的制度与文化

今年7月,Google在Communication of the ACM上发表了一篇文章,介绍Google独特的单代码库模式,题为《Why Google Stores Billions of Lines of Code in a Single Repository》[1]。顾名思义,与世界上99.99%的公司不同,Google将整个公司所有项目,数十亿行代码放在一个代码仓库中!作为一名Google的前工程师,笔者看了以后还是颇有感触的:一方面是对Google独树一帜的工程文化的一种怀念,另一方面是出来在外摸爬滚打了几年后,笔者对于公司间因为底层设计和制度造成的文化差异更是感触颇深。今天我们就从Google的单代码库模式,看看Google的工程文化有哪些独特之处。

从Google的单代码库模式看Google工程的制度与文化

差异

笔者从Google离职后,曾服务于国内某互联网公司广告部门。当时团队正在开发LBS方面的服务,已经应用在人群定向方面并取得了成效,后续计划将此服务集成到部门的展示广告引擎中,提供更加精准的基于地理位置的投送能力。于是笔者找到了该展示广告引擎的负责人,希望能够合作。对方态度非常友善,但表示开发资源实在紧张,引擎的需求列表已经排到下个季度了,这个feature虽然好估计也只能再往后排。上线一个功能先等3个月?这是笔者无法接受的。带着被Google熏陶的工程文化,笔者试探性的提出:如果是因为开发资源的问题,是否可以开放引擎的代码库,LBS相关的代码由我们提供资源代为开发,并遵循引擎的code review以及上线流程,共同完成LBS功能的开发和上线?对方大概愣了一秒钟,脸上泛起为难的表情,表示“这好像没有先例”。

是啊,世界上除了Google可能再也找不到一家有规模的公司会将所有项目的代码放在一个代码仓库,让所有工程师皆可访问、协作。大家所熟知的模式是公司里每个项目有一个独立的代码库,团队和团队间的代码“老死不相往来”,而上面的案例只是一件再正常不过的事情。但相同的事情如果发生在Google,则不会有这样的问题。LBS相关的代码可以马上开始,代码经过引擎团队审核之后就可提交。代码随引擎日常release部署上线,通过在线实验验证效果之后,基于LBS的新功能可以落地成功了。笔者曾在Google做AdSense广告相关性方面的工作,所熟悉的工作流程就是如此的,从没有因为“这部分代码不是你(们)负责的”而导致工作受阻。相反,公司上至GFS、Bigtable等这种基础服务,下至各种产品、工具,代码都是可见、可改的。因为工作原因帮相关产品或服务fix一个bug,加一个feature都不是难事,绝对是“自己动手,丰衣足食”。

协作是一方面,依赖有时也是个问题,特别是当一个项目是C++项目的时候。由于大家的代码不能共享,只能通过编译好的library进行依赖。通常安装每个依赖的library(以及它们依赖的library)就够烦的了,更烦的是这些library之间还经常有版本冲突,例如X依赖了Z的某版本v1,而Y偏又依赖了Z某另一版本v2,解决起来不胜其烦。在Google没有这个问题,大家都是代码依赖,通过同一套编译系统和编译环境进行编译和测试,有效避免不兼容的情况。新代码提交前,系统还会自动对被依赖的其它代码进行测试,不允许破坏被依赖代码的新代码提交。

揭秘

说完笔者在经历中感受到的一些差异,再来看看Google的单代码库模式究竟如何,有哪些特点。

Google把所有项目(除了个别开源项目外)放在一个代码库中,那么这个代码库有多大呢?看看[1]中提供的数字(这还只是截至2015年一月的):

9百万个代码文件
20亿行代码
共3500万次提交,工作日日均提交4万次
所有数据86TB

如此巨大的规模,必须有相应的配套设施和制度才能玩得转:

设施一:分布式代码库Piper和客户端CitC

Piper是Google自己开发内部使用的分布式代码库,基于Google分布式数据库Spanner [2],跨10多个数据中心,一劳永逸的解决了巨型代码库的scalability问题。在此之前,Google用的一直是Perforce,架在一台超级服务器上,居然也用了10多年。CitC全称Client in the Cloud,相当于Piper的客户端。

设施二:分布式编译系统Blaze

Google公开了一个该系统的开源版本可供参考,叫bazel [3]。这个系统在原文中提得不多,但个人认为它是整个生态链中不可或缺甚至最重要的一环。因为整个Google代码库有几百万代码文件,几十TB数据,加上大家是代码级依赖,如果按照传统的开发和编译方法,即使只check out直接和间接依赖的代码,其数据规模和编译时间也不可接受。而Blaze配合CitC可以做到开发时只需check out需要修改的代码,编译时将所有依赖进行分布式编译,编译上万C++文件也只需几分钟。Blaze还有一个好处就是为全公司提供了整齐划一的编译环境,使得编译器、三方库版本等都不再是问题。更多Google解读:www.yangfenzi.com/tag/google

设施三:代码浏览与检索系统CodeSearch

与通常的代码仓库一样,CodeSearch提供了一个代码仓库的Web访问界面。除此之外还提供了语法解析和索引功能,支持正则表达式搜索和类似IDE中的点击跳转到定义功能,给工程师们在庞大的代码库中寻找需要的信息提供了有力的支持。另外还支持在Web界面上对代码进行评论、吐槽:)

设施四:Code Review系统Critique

类似ReviewBoard,是一个做代码评审的平台。原文中提得也不多,个人认为也是Google整套生态链运作的基石之一,上面承载着Google代码管理的制度与文化,将在下面的制度中详述。

其它设施:代码重构工具Rosie,代码静态分析工具Tricorder等。[1]中都有所描述。

说完设施再说制度。笔者认为,相对于设施,制度可能更重要。下面这些制度并非为单代码库模式而生,但反过来不成立,即:如果没有这些制度,单代码库模式肯定玩不转。

制度一:严格的Code Review

前面说过,Google的工程文化是开放、协作的。但如果没有严格的代码评审制度,开放带来的就不是好处了,而是灾难。事实上,Google的代码评审严格到有时让人觉得苛刻的境界,与大多数其它公司的“代码评审”有很多本质上的不同:

未经评审的代码无法被提交到代码仓库。非常多的公司和团队是没有代码评审制度的,或者团队有代码评审制度,但所使用的代码仓库和code review工具却通常不是有机结合的整体,不能从技术上保证只有通过评审的代码才能被提交入库,久而久之代码评审容易半途而废或者“做不做看心情”。在Google,系统保证了只有经过Critique评审通过的代码才能被提交进代码仓库Piper。整个Google的代码库是树状目录结构,根节点下是各个大产品、基础模块的目录,下面又分各子模块目录,依此类推。每个目录都有几个“owners”,通常是相应产品或模块的Tech Lead或者资深开发人员,所有发生在这个目录下的更改必须在Critique中发送给他们进行review。一次更改通常会因为评审来来回回修改多次,直到owners在Critique中LGTM(“looks good to me”,是Google的文化之一)方能提交。惟其如此,单代码库下跨团队、跨项目开发协作的安全性才有所保障,开放的工程文化才能在无边界的代码库中驰骋。

代码质量与风格的标准严格且全公司统一。你可能听说过一则笑话,说Unix和Go语言之父Ken Thompson到了Google因为没过“语言测试”竟不能提交代码[4]。这个笑话不完全反映事实,但Google对于每种编程语言的代码规范[5],上至语言特性的使用,下至编程风格,都有着非常细致的规定,并且全公司执行一套标准,真正做到“车同轨,书同文”。每个工程师到Google第一件事情就是要掌握所用语言的标准并通过练习与测试。在做code review时,各reviewer也会事无巨细的针对系统的设计、实现的正确性、复杂度、代码风格和测试完整性等各方面一丝不苟的进行评审,通常代码更改几乎不可能一次性通过。而如果缺乏统一和详尽的代码风格标准,代码评审容易执行得参差不齐,久而久之流于形式,草率了事。没有统一代码标准通常也给跨团队、跨项目开发造成了困难。

制度二:严格的测试

测试从单元测试开始,每个新开发的类、函数或者逻辑更改必须包含相应单元测试。每次代码更改除了“生产性”的代码,必须包含单元测试代码,否则代码评审不可能通过。提交代码前,除了所更改代码本身的单元测试必须通过外,系统通常还会根据依赖自动执行所更改代码影响到的其它编译单元的单元测试,有任何一个单元测试失败,代码就不得提交。因此,在整个系统中,单元测试是基石,对于系统的健壮性有举足轻重的地位。如果你熟悉bazel或者诞生于Google的Go语言就会发现,单元测试是整个编译系统的标准组成部分,这里面体现的就是Google的测试文化。除了单元测试,各项目通常还有其它回归测试、性能测试等作为保障。以笔者之前所在的AdSense项目为例,回归测试和性能测试都是在代码提交前必须通过的,并且必须将结果附在代码评审的说明中以备查验。

制度三:Trunk Based Development

与许多公司和团队采用的“分支-开发-合并”开发模式不同,Google是主干无分支开发模式,即所有人都是在trunk上开发,所有更改直接提交到trunk上。主干无分支的开发模式相对其它开发模式无疑是最简单高效的,而且Google既然选择将全公司的代码放到一起似乎也没有其它选择(想象一下同时有成千上万个branch要把自己积攒的代码merge到trunk上会是什么景象…)。Google提倡小步快走模式,大的开发或逻辑更改也应尽量拆分成独立的小更改,超大的一次性更改在代码评审时也是不受欢迎的。

制度四:逻辑更改可配置化

除了之前所述的代码评审和测试制度保证了主干开发模式的稳定性外,这种开发模式实际上还决定了另外一种开发制度,就是一切逻辑更改可配置化。在这种开发制度下,任何对代码逻辑的修改不是覆盖原来的逻辑,而是保留原有逻辑的同时开发新的逻辑,并使用配置参数(命令行参数或者分层试验参数)控制代码运行时选择的逻辑分支。所有新逻辑的100%上线都必须经过canary deployment,即从1台机器、最小流量开始逐步验证,逐步上线。一旦发现新逻辑有任何不符合预期的问题,可以迅速通过配置参数进行回滚下线。以AdSense后端引擎为例,任何新逻辑都必须用分层试验系统[6]的实验参数保护起来,默认不启动(这在代码提交前通过回归测试进行了保证)。然后经过线上实验验证并通过发布评审后,方能完全上线新逻辑。新的逻辑上线一段时间,确定老逻辑已无必要后,可以对老逻辑代码进行清理。说到底,Google在开发流程中选择的不是在版本控制上用分支,而是在代码逻辑中用分支。

感触

说了这么多,其实Google的单代码库模式只是冰山一角,这背后是Google工程的一整套制度与文化。在这套制度与文化的土壤之上,团队与团队之间可以近乎无边界的协作,同时保持稳定、有序和坚如磐石的工程质量。这套制度与文化是如此的与众不同,正像Google两位创始人在IPO时说的“Google is not a conventional company, we do not intend to become one”。这就不得不让笔者感叹,也让笔者敬仰逐步奠定这套体系的Google工程师先贤们。有时,这竟让笔者联想到美国开国制宪的先贤们是如何在美洲大陆上奠定了一个与众不同又长盛不衰的国家。

然而正如所有制度与文化都有其对应的土壤,Google的制度要学习也未必就是那么容易的。就以最简单的单元测试为例:Google的工程师都有这个感触,每次花在写单元测试上的时间几乎总是和写“生产性”代码的时间差不多,有些时候甚至还超出不少。再算上code review一般来来回回三四趟的时间,在Google开发一行“生产性”代码用的时间可能是“写完逻辑就提交”公司的几倍。这在大多数公司可能就是冒天下之大不韪了,因为在同一家公司里通常很难通过对比去证明上线前用于工程质量的确定性时间投入会比上线后遇到bug和解决问题所需的可能性时间投入要少。所以大家通常的选择是避免确定性的投入,习以为常的是“先尽快上线,有问题在线上去发现和解决”的工作方式。

不过虽然大家面对的环境和问题不尽相同,Google的一些制度、文化以及理念可能或多或少还是值得大家借鉴一二的。特别是当你想打造一支新的团队或建立一个新的公司时,你的选择会成为它基因,决定它今后的样子。

[1] http://delivery.acm.org/10.1145/2860000/2854146/p78-potvin.pdf
[2] Corbett, James C., et al. “Spanner: Google’s Globally Distributed Database.” ACM Transactions on Computer Systems 31.3 (2013).
[3] http://www.bazel.io/
[4] https://news.cnblogs.com/n/62440/
[5] Google所使用的大部分语言的代码规范已经公开:https://github.com/google/styleguide
[6] Tang, Diane, et al. “Overlapping experiment infrastructure: more, better, faster experimentation.” Knowledge Discovery and Data Mining (2010).

文/帅到自然醒

氧分子网(www.yangfenzi.com)是关注互联网生态圈的科技新媒体

·氧分子网http://www.yangfenzi.com)延伸阅读:

➤ 为什么很多优秀的软件公司和开发者愿意开源和共享?

➤ Google下一代跟踪代码管理器 Firebase 闪亮登场

➤ 解读 Larry Page 的 Alphabet:想要的是从 A 到 Z 的一切

➤ 谷歌和Facebook两公司有何不同:企业文化很不一样

➤ 一个充满智慧的地方,从 Google 学到的厕所文化

➤ 铂诺理财联合创始人吴卓浩:Google的T-shirt和阿里的月饼

➤ 低调铸就辉煌:Google新CEO Sundar Pichai “劈柴”究竟何许人也?

分享给您的好友:

您可能还喜欢…