谷歌的内部开发工具是世界领先的,其针对大规模软件开发的多方面痛点提供了解决方案。但几乎所有工具均与谷歌独有的内部生态系统紧密耦合,无法在其它环境中使用。本文介绍了如何在软件开发中引入好的开发工具,提高自己和团队成员的生产力,进而在大规模软件开发中传播有效的最佳实践,为公司带来工程化效率提升。
本文最初发表于about.sourcegraph.com(《An ex-Googler's guide to dev tools》),经原作者授权,由 InfoQ 翻译并分享。
多年前我曾在谷歌短期任职。尽管此后历经沧海桑田,但在谷歌期间接触其内部开发工具的经历,对我产生了长远的影响。从很多方面看,谷歌的内部开发人员工具是世界最领先的。谷歌不仅在自身软件系统的扩张上走在了前列,而且在大规模软件的高效构建方法上也是领先的。谷歌针对代码库规模、代码可发现性、组织知识的分享以及多服务部署等方面的问题提供了解决方案,达到了大多数企业尚未企及的高度。作为参考,推荐《谷歌软件工程》(“Software Engineering at Google”)一书。
但从另一方面看,谷歌的内部工具是非常有局限的。事实上,几乎所有此类工具均与谷歌独有的内部生态系统紧密耦合。这意味着人们一旦离职,很不幸就无法在其它环境中使用这些工具。
尽管如此,这些才华横溢的谷歌离职人员汲取了在世界领先技术组织工作中的经验教训,进而为其他许多组织注入了新的动力。但适应谷歌之外的编程开发环境并非易事,尤其是他们已经形成依赖的一些工具没办法在使用了。
这些年来,我从自身以及许多其他离职谷歌的人身上学到了不少。Sourcegraph 的许多早期客户,就是因为离职谷歌后想念原公司的代码搜索功能而找到了我们。通过与客户的紧密合作,我们了解到他们迫切需要填补的空白,进而去构建 Sourcegraph 的功能来满足他们的需求。谷歌前员工正在探索如何在当前组织中使用新开发工具的模式。这一工作的灵感源自于他们使用谷歌开发工具而具备的经验。当然,一些探索是成功的,也有些折戟沉沙。
就此问题,我认为撰写一份着眼于实操和实用的外部开发工具指南是非常有意义的。能将谷歌的内部开发工具生态系统直接克隆到新公司中,无疑是不少谷歌前员工的愿望,但也应切忌好高骛远。下面我就会谈谈我的看法,讲一讲前谷歌员工如何开始寻找让他们和他们的新团队尽可能高效工作的工具。
软件开发的生命周期
对于刚离职谷歌并加入其他公司的人而言,可能一时难以适应大不如前的工作效率。尽管大家都觉得需要做出一些改进,但应从何入手呢?第一步需要认真考虑的,是如何从日常工作中发现真正的痛点所在。
无论对于谷歌内部还是其他组织来说,软件开发的生命周期基本都是这个样子:
列出需构建的特性,或是需要修正的软件缺陷;
通过大量阅读代码和文档,以及与同事开展交流,建立对问题的认识,并给出一个大体适合现有系统的解决方案;
着手编程工作。首先做出来能运行的东西,期间可能需要反复地查看文档及部分代码。
一旦代码达到能运行的程度,这时不要急于交付。做代码测试,修复缺陷并做进一步测试。进而重构代码,生成整洁并便于接手者理解的代码。将代码推送到代码库生成分支,等待运行持续集成。期间的代码可能实现了一些额外修复和小部分改进。
提交供审核的代码补丁,根据团队成员给出的评论进行更改。这一过程可能需反复数轮,直至代码审核人员通过更改。
归并补丁,并做部署。
监控已部署系统的运行情况,判定生产环境中是否存在问题。如果新打的补丁导致系统宕机,负责修复问题。
这一过程中的每个阶段,都需要在适用的开发工具辅助下开展。开发工具引导开发人员按章行事,主导着工作流程,控制着工作效率。
选定适合的工具,才能提高开发效率。我推荐一个 Github 代码库,地址为https://github.com/jhuangtw/xg2xg。其中列出了近乎所有的谷歌内部工具,以及具备对应功能的外部工具。列表非常详尽,但是略为冗长。
开始阶段:熟悉现有工具,不要引入新工具
我们在刚参与到一个项目中时,不要试图对现状做任何改变,只需萧规曹随。
做为一名团队中的新人,不太可能有权或能影响整个团队去迎合你个人对工具的喜好。此外,你也缺少对团队做事方式和事情前因后果的了解。生搬硬套谷歌那一套,也许并不适用于新团队。因此,首先是要领悟新团队的工作方式和禁忌。
从易于改进之处着手
我认为首先可加以改进的,就是代码搜索功能。鉴于我本身就是一家代码搜索企业的联合创始人,当然会这么认为。同时,下面给出更具说服力的原因。如果读者并不认同,大可跳过此节。
代码搜索是谷歌离职员工通常缺失的日常工具之一。
你可以自己尝试各种代码搜索引擎,找出确实好用的选项再给别人推荐。也就是说你不需要得到领导的许可,也不用冒搭上自己人品的风险去说服他人尝试你自己都没用过的工具。
大多数团队尚未使用代码搜索工具,因此不存在强迫别人改变现有习惯的问题。如果团队已经在频繁使用很好的代码搜索工具,尽可跳过本节!
新公司中可能有多个团队,这时我们难免会处理超出个人合理能力范围的代码。即使在一家规模较小的公司工作,我们也有可能会通过依赖项获取大量的开源代码。在构建新功能时,或是追踪某些严重错误的来源时,一些情况下需要深入研究所有这些代码。
考虑到当前几乎所有开发人员需面对的代码规模,无疑低效的代码搜索会严重阻碍开发的进度,导致步步维艰。
选择代码搜索引擎时,需考虑如下因素:
查询语言:正则表达式是标配。确保代码搜索查询语言具有很好的表达力,并易于使用。提供直观的按词搜索,并提供高级的模式匹配功能。
扩展性:确保代码搜索引擎适合代码库当前的规模。如果代码库规模达数个 GB,需考虑搜索引擎是否支持三元词索引技术。该技术适用于大规模代码库中的正则表达式匹配。
代码浏览:使用过 Google Code Search 的人都明白,搜索只完成了部分工作。查看搜索结果时,类似于在 IDE 开发环境中查看代码一样,需要支持跳转到定义功能,并便于查找引用。如果代码浏览功能不够强大,那么就需在编辑器和搜索引擎之间频繁切换。
权限:如果企业强制了代码库的权限,需考虑代码搜索引擎对权限的适配性。
整体代价:需考虑部署代码搜索引擎的代价,以及在线使用的整体维护代价。
当前人们使用的主要代码搜索引擎包括:
OpenGrok:Oracle 的产品,最具历史,也一直在用。
Hound:一款由 Etsy 工程人员创建并开源的代码搜索引擎。
Livegrep: 由 Stripe 的 Nelson Elhage 创建的代码搜索引擎。
当然,还有我们的Sourcegraph。
良好的监控
监控是另一个需要考虑尽早改进的方面。工程师有时候必须去处理生产环境中出现的问题。但生产是与开发截然不同的,无法通过设置断点或直接添加 printf 而在数秒内看到效果。从计算资源、开发人员时间、以及最糟糕的是给用户和客户带来痛苦等多个方面看,生产环境中做更新的代价尤其高昂。
部署在过去的五到十年间发生了巨大的改变。微服务、Kubernetes、云端迁移等技术,极大地改进了企业的软件部署方式。很多企业采用了这些新方式和新技术,但尚未相应地更新监控架构来方便地调试新的生产环境。
好消息是现在已经有了一些很好的开源工具和企业,极大地改进了谷歌之外的监控和可观察性现状。
Prometheus:一款对标 Borgmon 的时序度量追踪和可视化工具。为用户提供仪表盘显示的应用追踪度量,例如 CPU 使用、错误率、p90 延迟等随时间变化的情况。
Grafana:一款对标 Viceroy 的仪表盘工具。常用场景是连接到 Prometheus,构建在单页上显示一系列关键指标的视图,展示应用的整体健康情况。
分布式追踪是日益普及的多服务架构的必备工具,对此,Google Dapper 居于领先地位。Lightstep是由 Dapper 的创建者之一 Ben Sigelman 推出的项目。分布式追踪已是许多监控系统提供的特性,包括付费工具Honeycomb和Sentry等,以及 Uber 工程师推出的开源工具Jaeger等。
考虑到监视必须集成到生产环境中,因此要比引入代码搜索更具难度。引入监视需更改部署环境,这意味着要说服管控部署环境的团队。监视还可能需要添加仪表盘代码,这涉及向所有仪表盘代码相关团队提交补丁。但引入此类新工具并不需要任何人改变现有的习惯,从某种意义上说也并非不可为之。人们可以自由选择是否使用新工具,这可避免在推行新工具时面对强烈的反对意见。
步步为营:代码审查
引入代码搜索和监控,并不会更改其他团队人员现有的工作流程。但是改进代码审核工具,则需大家配合。
对于具有谷歌工作经验的人而言,很有可能不太适应谷歌之外的代码审查方式。对于常用的代码审核工具 GitHub Pull Request(PR),抱怨集中于以下几点:
不够直观,有时无法查看自上一轮审核以来所做的更改。简单路径仅支持查看显著差异;
不支持积压的更改请求(Stacked CR);
在同一页中整体显示所有文件的全部差异,难以追踪已审核项;
GitHub PR 的审核实现方式毫无特点(unopinionated)。如果不额外添加第三方集成,审核过程松松垮垮。即便添加了第三方集成,依然缺乏强制的细粒度审核和签出策略功能;
尽管对部分语言提供对模糊“跳转到定义”(jump-to-def)和“查找参考引用”(find-references)的有限支持,但远未达到谷歌内部使用的 Critique 的水平。
与 Critique 最接近的谷歌之外工具是 Gerrit。Gerrit 最早是 Rietveld 的一个分支,而 Rietveld 本身是谷歌最初代码审核工具 Mondrian 的一个开源分支。因为工具线的传承,二者看上去非常相似,设计用于创建谷歌支持的代码审核方式。
Phabricator 是谷歌前员工喜欢替代 Github PR 的另一款工具。Phabricator 一开始是 Facebook 的代码审核工具,随后开源并对外发布。该工具由Phacility公司支持,为不想自己维护实例的用户提供托管实例和服务支持。
由谷歌前员工 Piotr Kaminski 创建的 Reviewable 是另一款值得推荐的工具。不同于 Gerrit 和 Phabricator,Reviewable 仅用于云端,提供类似于谷歌内部的代码审核体验。
要向团队其他成员推荐 Gerrit、Phabricator或 Reviewable 的优点,重要的是指出团队现有代码审核工具在使用上的痛点。下面给出由 Github PR 类工具转向类 Gerrit 工具所解决的部分痛点:
Gerrit 提供明确的签发(sign-off),有助于审核过程更加结构化。如果系统扩大团队并在整个组织中强制更严格的审核策略,该特性非常好用;
Gerrit 便于审核大量差异,支持对逐个文件、上一轮审核后的更改以及积压 CR 的审核,提供更快、更全面的审核。
Gerrit、Phabricator 和 Reviewable 可实现类似谷歌内部的审核流程,但都尚未提供可对标的代码智能功能。如果当前代码审核工具中并不具备代码智能,或是发现 GitHub PR 中缺失代码智能,可尝试Sourcegraph的浏览器扩展。它连接 Sourcegraph 实例,为代码审核提供工具帮助、跳转到定义和交叉引用,支持 GitHub PR、Phabricato 和 Bitbucket 服务器,正实现对 Gerrit 的支持。
准备屠龙大招
软件开发生命周期中最棘手的部分,通常是持续集成和构建系统。因为要理解构建,通常需要细致理解整个代码库的每一部分。加快构建速度是所有人的愿望,因此大家在构建代码中越来越多地使用了一些技巧和优化措施,进而导致真正能确保当前进展中所做更改不会产生任何负面影响的人数屈指可数。
简而言之,构建系统通常千头万绪(giant hairball)。在尊重底层开发人员提高开发效率的做法的同时,需慎重地逐一厘清。想要早发现苗头早解决的话,Blaze 是最好的工具,谷歌甚至为 Blaze 的衍生产品 Bazel 开源提供帮助。但 Bazel 终究并非 Blaze,谷歌外部环境也并非适用谷歌的工具。举一个例子,Blaze 中缺少在 Bazel 中打包提供的大规模分布式构建集群功能。
Bazel 也并非灵丹妙药(silver bullet)。在 Bazel 首次发布时,Go 社区中的很多开源项目出于对标准 Go 构建工具的喜爱而纷纷转向使用 Bazel。但在一年内,面对 Bazel 的复杂性和难以上手的缺陷(并且看上去使用 Bazel 的构建速度也较慢),很多项目又转回 Go 社区。虽然当前 Bazel 对 Go 的支持已做了很大的改进,但在转向使用它时,还是需要对所获得的改进做出认真的评估。
开展严格的评估,需要手头有一些好用的开发工具。尤其需要很好的代码搜索工具,这样才能切实深入研究代码库各个部分的构建脚本,理解它们的来龙去脉。还需要很好的代码审查工具,因为更改构建系统是一项复杂的事情,需要多个不同工程团队的支持。
一旦准备好屠龙,在 Bazel 之外还有其它一些从设计上支持大规模代码库中可扩展构建的工具。包括:
Facebook 提供的Buck;
Java 领域广为使用的Gradle;
Pants,由一名谷歌前员工为 Twitter 和 Foursquare 开发;
Please,也是由谷歌前员工新推出的构建工具,主要借鉴了 Blaze。
还有同是谷歌前员工 Yves Junqueira 推出的YourBase。YourBase 本身并非构建工具,而是一款持续集成工具,独立于后台使用的具体构建工具,在谷歌之外提供快速、可扩展的构建。
总结
谷歌独树一帜地提供了优先考虑开发人员经验和开发人员工具,使得谷歌员工和前员工能受益于使用一流开发工具而获得的一手经验。这些工具极大地影响着他们的天赋和能力。
一旦离开谷歌,对这些经验的利用就成为一种竞争优势。人们可以将出色的新开发工具带入新组织,提高自己和团队成员的生产力。通过使用这些工具,在大规模软件开发中传播有效的最佳实践,可为新公司带来组织的有效工程化,这是谷歌的一项主要竞争优势。
诚然,大规模软件构建绝非易事。正如《人月神话》(The Mythical Man Month knows)一书所说,好的软件并非通过雇佣更多开发人员就能实现。鉴于软件是最终用户生产率的倍增器,而开发工具是软件构建人员生产率的倍增器,我们需要更好的工具。如果你认可新企业的理念,那么就发挥做为谷歌前员工的独到见解,为新企业带来最好用的开发人员工具。
评论