研发效率破局之道05 | 代码入库前:Facebook如何让开发人员聚焦于开发?

你好,我是葛俊。今天,我将与你分享优化流程中,代码⼊库前的开发流程。 
代码⼊库之前的开发活动,主要包括编码、调测调优、静态检查、⾃动化测试、代码审查等。这是开发者编写代码的步骤,⾃然是提⾼研发效能的关键环节。 
图 1 本地开发流⽔线 
提⾼开发者编写代码的效能,关键在于让开发者不受阻塞、不受不必要的⼲扰,从⽽全身⼼地聚焦在产品开发上。我把这种不受阻塞的开发状态叫作持续开发。 
⼀个团队如果能够做到持续开发,那么它的有效产出⾃然会很好。⽽对于个⼈开发者⽽⾔,持续开发能够帮助我们把精⼒集中在技术本身,对技术和个⼈能⼒ 的提升都⼤有裨益,所以是⼀种很好的开发体验。 
在我看来,持续开发的基本原则主要包括两条: 
1. 规范化、⾃动化核⼼步骤; 
2. 快速反馈,增量开发。 
接下来,我们就⼀起看看这两条核⼼原则吧。 
规范化、⾃动化核⼼步骤 
要让开发者聚焦于开发,就必须把研发流程中可以⾃动化的步骤尽量⾃动化。因为⼀般不可能完成所有步骤的⾃动化,所以我推荐的⽅式是:分析关键路径上 的活动,以及耗时较⻓的活动,然后投⼊精⼒优化这些步骤。 
⾸先,我们需要明确具体的开发步骤有哪些。我将其归纳为以下三⼤步: 
1. 获取开发环境,包括获取开发机器、配置环境、获取代码等。 
2. 在本地开发机器上进⾏开发,包括本地的编码、调测、单元测试等。 
3. 代码⼊库前,把改动提交到检查中⼼(⽐如 Gerrit),再进⾏⼀轮系统检查,主要包括代码检查、单元测试、代码审查等,通过之后再⼊库。 
图 2 代码⼊库前的 3 ⼤开发步骤 
针对这三⼤步骤,我们可以有以下 3 个⼯程实践:
  • 提高开发环境的获取效率;
  • 规范化、自动化本地检查;
  • 建设并自动化代码入库前的检查流程。
接下来,我们分别看看这 3 个⼯程实践如何落地。 
提⾼开发环境的获取效率 
开发环境的设置,包括开发机器的获取、⽹络配置、基本⼯具以及代码的获取和配置。这些操作的频率不是特别⾼,但如果步骤复杂、耗时⻓,就会对新成员 加⼊、成员切换团队或者项⽬,产⽣⽐较⼤的影响。所以,开发环境的获取效率,通常是值得优化的。 
有⼀个可以采⽤的优化⽅式是,把整个开发环境的获取,进⾏服务化、⾃助化。也就是说,开发者可以⾃助地申请获取环境,不需要 IT 部⻔的⼈员介⼊,从 ⽽既节省了开发者的时间,⼜降低了 IT 部⻔的⼈⼒成本。
⽐如,我之前在 Facebook ⼯作的时候,采⽤虚拟机作为个⼈开发机。内部⼯具团队开发了⼀个基于共享机器池的开发环境服务系统,让开发者可以在⽹⻚ 上申请和释放机器。机器返还之后,开发环境服务系统会⾃动对它进⾏清理,配置之后再重新放回机器池中。这就使得开发者可以在 5 分钟之内拿到⼀套⼲ 净的环境。 
⽽⾄于开发机器上的代码,这个服务系统可以克隆获取团队常⽤的代码仓,并定时拉取最新的代码。这就使得开发者拿到⼀台机器之后,只需要再额外拉取很 少的代码就可以进⾏开发。 
上⾯这种⽅法定制性很强,Facebook 并没有开源。如果你准备进⾏这⽅⾯尝试的话,在机器的⽣成和配置⽅⾯,我推荐两种⽅式。 
第⼀种⽅式,借助基础设施即代码(Infrastructure as Code,IaC)系统。⽐如,HashiCorp 公司的Terraform ⼯具。它⽀持声明式的⽅式快速产⽣⾃定义 配置的机器,并在上⾯运⾏脚本进⾏配置。TerraForm 使⽤插件机制⽀持许多底层平台,⽐如 AWS、阿⾥云或者本地系统。 
这种⽅式的优点是使⽤⽅便、功能强⼤,但前期投⼊⼤。 
第⼆种⽅式是,提供机器镜像和配置脚本。通过镜像让每⼀台新机器拥有最基本的设置,⽐如 CPU、操作系统、基本软件,然后通过脚本实现基本配置,⽐ 如⽹络设置、软件更新等。这种⽅式的优点就是,前期投⼊⼩。我在 Stand 时,就使⽤了这种⽅法,效果不错。不过它的缺点就是不够灵活。 
规范化、⾃动化化本地检查 
本地检查是指,开发者在开发机器上进⾏的验证,⽐如语法检查、规范检查、单元测试、沙盒搭建等。我推荐的⽅式是,根据团队实际情况,找到合适的⼯具 和配置进⾏这些检查,并让团队成员统⼀使⽤。 
在这个⽅⾯,Facebook 的⽅法是,把很多⼯具都放到⼀个⽹盘上,挂载到每台开发机器的 Linux ⽂件系统上,让开发者们不⽤安装就可以直接使⽤。 挂载共享⽹盘的⽅法⾮常⽅便,因为⽤户不⽤操⼼⼯具的升级。但如果你们的系统没有这样的⽹盘的话,也可以通过脚本让开发⼈员⼀键安装⼯具和完成配 置,效果也不错。缺点就是软件更新⽐较麻烦,因为要通知⽤户⼿动更新或者设计⾃动更新机制。 
⾄于检查中使⽤的⼯具,我们需要根据具体的语⾔和框架去选择。 
建设并⾃动化代码⼊库前的检查流程 
建设并⾃动化代码⼊库前的检查流程,是持续集成前的必要⼯作,也可以看作是持续集成的⼀部分。它对⼊库代码质量起到⼀个⻔禁作⽤,对提⾼质量⽤处很 ⼤。我认为,除了⼈数⾮常少的初创公司以外,其他开发团队都应该进⾏这个配置。 
这个流程⼀般可以使⽤代码仓管理系统作为中⼼,直接使⽤或者通过钩⼦集成其他⼯具和系统来实现。⽐如,使⽤ GitLab 提供的 GitLab CI/CD 框架。基本 ⽅法是,在项⽬的根⽬录⾥创建⼀个.gitlab-ci.yml ⽂件,来描述你的检查环境设置和步骤。你可以点击这个链接查看具体的⽅法。 
在 Facebook,这⼀步使⽤的是开源版 Phabricator 的⼀个内部 Fork。Phabricator 在⼯作流中使⽤单元测试和 Linter 的⽅法,你可以参考帮助⽂档。 
以上内容就是持续开发的第⼀个原则,也就是规范化、⾃动化核⼼步骤。这个原则,可以帮助开发者尽量减少⾮开发⼯作的耗时,从⽽把更多的时间、精⼒投 ⼊到本职的开发⼯作中。接下来,我们再来看看持续开发的第⼆个原则,即提供快速反馈,促进增量开发,这样能及早暴露问题,从⽽保证将来的⼯作不会因 为实现错误,或者⽅向调整⽽进⾏昂贵的修改。 
提供快速反馈,促进增量开发 
提供快速反馈,进⾏增量开发指的是,能够快速验证已经完成的开发⼯作,说⽩了就是边开发边验证。具体的⼯程实践主要包括以下 3 个: 
  • 灵活使⽤各种 Linter 和测试; 
  • 建设并优化沙盒环境;
  • 使用实时检查工具。
接下来,我们分别看看这 3 个⼯程实践如何落地。 
灵活使⽤各种 Linter 和测试 
最常⽤的快速验证⽅法就是,提⾼运⾏静态检查和测试的⽅便性、灵活性。各种语⾔、框架都有⾃⼰的测试框架和 Linter,这⾥我就不再⼀⼀列举了。接下 来,我会与你分享两种通⽤的有效使⽤ Linter 和测试的⽅法。 
⾸先,⽤命令⾏的⼯具来封装各种检查。命令⾏⼯具特别适⽤于⾃动化,⽅便开发⼈员使⽤。⽐如,我们可以通过命令⾏脚本,来实现简单的⼯作流。 
举⼀个具体的例⼦,我希望团队在开发中,在运⾏公司提供的统⼀检查之外,还可以运⾏⼀些适应团队⾃身特点的检查,每个开发⼈员也可以添加⾃⼰希望使 ⽤的检查。这样就可以通过⼀个简单的Shell 脚本,依次调⽤公司的、团队的、个⼈的检查来实现,很⽅便。 
其次,以服务化的⽅式,把这些检查的能⼒提供出来。⽐如,Facebook 的基础平台团队提供了在云上运⾏单元测试的能⼒,并把这个能⼒通过服务的⽅式提 供给开发者,以⽅便他们在⾃⼰的开发机器上调⽤。也就是说,开发者可以调⽤云上资源运⾏⼤量的测试⽽不占⽤本地资源,从⽽在运⾏测试的同时可以⾼效 地进⾏开发⼯作。  
建设并优化沙盒环境
沙盒也是⼀个⾼频使⽤的、提⾼质量的⼯具。开发者如果能够⽅便地在本地搭建沙盒进⾏验证,那么进⾏开发⾃测的频率和质量就会⼤⼤提⾼,进⽽提⾼产品 质量。所以,我推荐你在沙盒环境的搭建上进⾏投⼊。 
在沙盒环境搭建中,有两个常⻅的优化点: 
本地构建。因为我们必须把改动构建成产品才能进⾏本地验证,⽽这个步骤通常耗时较⻓。我推荐的优化⽅法是,不要使⽤全量构建,尽量只进⾏最⼩范围 的增量构建。 
测试数据的产⽣。产⽣贴近⽣产环境的数据往往⽐较费劲,Facebook 的做法是,开发环境直接使⽤⽣产环境的数据,不过这个⽅法⽐较激进,使⽤的公司会⽐较少。另⼀个常⻅⽅法是,进⾏⽣产数据的导出并脱敏,然后使⽤到沙盒环境中。 
使⽤实时检验⼯具 
快速提供检查反馈,做到极致就是开发者⽆需⼿动触发检查,⼯具就会⾃动探测到改动、⾃动运⾏检查。最常⻅的是,IDE 中的实时语法检查。我们可以花⼀ 些时间来配置 IDE。另外,有些⼯具可以⾃动监视⽂件系统的变化,⽂件有变化时⾃动重启服务。这对于开发者来说,⾮常便利。 
举个例⼦。使⽤ Node.js 进⾏开发时,nodemon 就是不可或缺的⼯具,你只要在原来的命令前,加上 nodemon 就可以使⽤。⽐如,启动服务的语句 是./bin/www,使⽤ nodemon 的形式就是 nodemon ./bin/www。这样运⾏服务之后,如果你的⽂件有修改,nodemon 就会⾃动重新运⾏。 
你可以在下⾯这个动图中看到,在第⼀次保存时,有语法错误,nondemon 重新启动失败;第⼆次保存时修复了语法错误,nodemon 成功重启服务。通过 nodemon,我减少了两次⼿动重启服务的繁琐操作。 
图 3 nodemon 使⽤示例 
类似的⼯具,SpringBoot 有 Spring-Boot-Devtools,你可以点击这个链接查看详细描述。针对 Python,可以直接使⽤ nodemon,这⾥也有⼀个链接供你 参考。 
如果你使⽤的框架、语⾔没有直接可以使⽤的⼯具,帮助你进⾏实时重启服务的话,可以使⽤类似watchdog/watchmedo的⼯具来实现⾃动化。⽐如 
watchmedo shell-command
 –patterns=”*.py”
 –command=’python “${watch_src_path}”’
 .
会监控所有的 Python ⽂件改动并⾃动重启。 
提供快速反馈,边开发边验证,虽然只是⼀个简单的原则,但可以带来很多好处。最直接的收益就是,能够⼤⼤提⾼开发者对当前代码的信⼼,从⽽促进代码 尽早⼊仓、尽早集成。 
可能你也注意到了,代码集成越晚发现问题就越晚。这正是产品上线的最后关头合并混乱,产品质量差、返⼯率⾼的⼀个重要原因。所以,我建议在你的⼯作 流程中,要尽量提⾼实时验证的能⼒。如果你这么做了,很快就会看到效果。 
⼩结 
在今天这篇⽂章中,我和你分享了两条持续开发的基本原则,来帮助开发者在代码⼊库前聚焦于开发⼯作:⼀是,规范化、⾃动化代码⼊库前的核⼼步骤;⼆ 是,提供快速反馈,帮助开发者边开发边验证,以促进增量开发。 
我将今天的内容,总结为了⼀幅图,帮助你复习。 
图 4 代码⼊库前流程优化 
这些原则和实践,是我根据⾃⼰的经验总结出来的。如果能直接适⽤于你的团队当然最好了,但我更加希望的是,你能从这些原则和实践的讨论中,理解它们 背后的思路,从⽽找到合适的⽅法和实践,去优化代码⼊库前的流程中最需要优化的地⽅,让开发者能够真正聚焦于开发。 
另外,我给你⼀个落地持续开发的⼩贴⼠:持续开发很适合⽤⾃上⽽下和⾃下⽽上相结合的⽅式来推动。因为开发者最了解⾃⼰⼯作的痛点,所以也能⽐较准 确地找到需要优化的地⽅。在 Facebook,很多⼯具和流程都是由开发者⾃发开发或者引⼊,后来逐步推⼴⾄团队和公司使⽤的。 
所以我推荐,作为开发者,你可以⾃⼰抽⼀点时间优化⾃⼰的⼯作流程,⾃动化繁琐的⼯作;⽽作为管理者,你可以有意识地奖励这样的优化⾏为,并对适⽤于团队的部分进⾏推⼴。 
思考题 
最后,我来给你留下两个思考题吧。 
1. 在开发环境⽅⾯,你有没有尝试过在 Docker ⾥⾯进⾏开发?你觉得这种⽅式的好处是什么,弊端⼜是什么呢? 
2. 有些开发者喜欢写好⼀个⽐较⼤的功能单元,然后再⼀⼝⽓调测。你觉得这样做的好处和坏处,各是什么呢? 
感谢你的收听,欢迎你在评论区给我留⾔分享你的观点,也欢迎你把这篇⽂章分享给更多的朋友⼀起阅读。我们下期再⻅!

发表评论

您的电子邮箱地址不会被公开。