管理@IT·互联网

如何提高一个研发团队的“代码速度”?

2018-07-24  本文已影响110人  雪吹西门诗

什么是代码速度(Code Velocity)?

Code Velocity的定义是:一段代码变更,从git里的commit time,到在生产环境里运行,中间经过了多少时间。换句话说,代码从写完开始,多快能到达生产环境。

举个例子,C公司的一个团队,他们今天的code velocity一般在是2-4周左右:

他们的一个典型的迭代周期是4周⁽¹⁾:第一周系分测分,第二、三周coding、testing、修bug,第三周末或第四周初合并回master、部署集成测试环境、跑回归、上预发、上生产环境。在这样的迭代节奏和“分支开发、主干发布” ⁽²⁾ 的模式里,从commit time到进生产环境,平均是2周左右。

他们还有一些比较长周期的项目。例如,有几个项目是四月中上旬拉的分支,一直到五月下旬才合回master,六月初发布上线。从四月上旬到五月下旬,这几个项目分支里的代码没有合回master过。这几个项目的code velocity就比较长,平均是4周左右。

为什么要度量和提高Code Velocity?

Code velocity体现的是一个研发团队快速响应业务需求的能力。

以上文C公司这个团队今天的快速响应、交付的能力水平,在两周一次发布窗口的节奏里,大部分时候可能已经够了,但一旦遇到各种意外,就捉襟见肘了,例如:临时封网,需求变更,项目因故延期等。

快速响应、快速交付的能力要有一定的“储备”,这就好像足球运动员要有体能储备:要想赢下加时赛,就要有踢两个加时赛的体能。研发团队要能在两周一次发布窗口的节奏里游刃有余,就要有一周一发甚至一周两发的能力。况且,可以预见在不远的将来,两周一次的发布窗口也嫌太久了,业务压力会倒逼一周一发成为常态。那时候,这个团队就要有“天天发”的能力,才能游刃有余。

研发团队的code velocity和他们拿到的业务结果之间的关系,就像饭店上菜时间长短和生意火不火之间的关系一样,两者是相关的,但不是强因果关系:

一家饭店要火,还要看地段、装潢、菜单、原料、厨子、服务员、宣传等。

除了快速响应业务需求以外,提高code velocity还能帮助开发和测试同学降低项目并发、减少上下文切换、提高幸福感。在两周一次发布窗口的节奏下,很多时候研发同学把一个需求写完、测完,要等其他需求,等集成环境测试,再回来搞一波,然后到了生产环境发布再回来搞一波。事情是不连续的,开发测试其实是被打断的。Code velocity提高了以后,开发测试有连续性,写完了测完了的代码就发走了,研发同学也不用身上同时背着一串项目了。

如何提高一个研发团队的“代码速度”?

为什么Code Velocity快不起来?

仔细想想,一段代码从git commit到生产环境,这个过程中时间大部分是花在等待上的:等着和其他代码一起发布上线。之所以会要把很多代码合到一起,每两周发一次,是出于cost vs. benefit的权衡:

首先,由于采取了“分支开发、主干发布”的模式,代码要从各个项目分支和迭代分支合并回master,要解决冲突,确保合并时没有漏代码。

然后,要对master里的代码跑一次全量的回归:准备环境、部署代码和配置、执行回归测试用例、分析结果。这个过程做一遍,短则半天一天,长则两三天甚至更长。如果发现问题,需要修bug,这个过程还要再重复。

与此同时,有些团队还要写发布计划,详细列出发布的步骤:要改哪些配置,各个系统的发布顺序是什么,回滚的步骤是什么,等等。发布计;划写好了还要评审。

最后,要走一遍发布流程:先上预发,上去以后QA要做预发验证;上生产环境,按照发布计划一步步做,蓝绿切流的过程中要让各个系统的owner确认OK,再继续蓝绿切流。整个发布过程需要很多人的协同。

例如,X系统的API增加了一个新参数,要求Y系统在调用这个API的时候必须要传这个参数。如果两个系统上的代码变更一起发(而且是蓝绿发布),就比较简单。但如果把这个工作拆解成小块,开发工作就变复杂了:X的API新增的这个参数必须先做成optional的,等Y那边的代码改好发上线了以后,再把X的这个新参数改成required。

另外,在有些实际项目中,实际情况比上面举的这个例子更复杂,并不是那么容易一眼就能看出来怎么拆解的。

如何提高Code Velocity?

要提高code velocity,就要对上面提到的这些原因对症下药,提升四个关键能力:

  1. 能频繁地把代码合回master
  2. 非常强大的跑回归的能力
  3. 一键部署乃至无人值守发布的能力
  4. 把大项目拆成小项目做的能力

提高code velocity,要实现质的飞跃,第一个能力“能频繁的把代码合回master”是关键抓手。把这个能力建设好了,提升code velocity的四个关键能力中的三个就具备了,因为“能频繁地把代码合回master”有三个前置条件:

  1. 实行了代码门禁
  2. 有非常强大的跑回归的能力(即上面四个关键能力的第二个)
  3. 把大项目拆成小项目做的能力(即上面四个关键能力的第四个)

代码门禁(Gated Checkin)

代码门禁能够确保每一个进入主分支⁽³⁾的commit都达到了一定的质量标准,例如:编译必须通过,单元测试和接口测试必须通过,新代码的覆盖率不能低于某个水平,静态代码扫描必须通过,等等。其实今天很多公司已经有post-checkin的CI在跑这些检查项了。代码门禁看似平淡无奇,无非就是把这些检查项从post-checkin挪到了pre-checkin。但别小看这一挪,它的效果,不亚于把“当月业绩决定本月提成”改成“当月业绩决定下月提成”的效果。

代码门禁是很典型的“测试左移”的做法,和我们对质量的基本规律的认知也是一致的:问题发现得越早,修复起来代价越小。实施了代码门禁后,能确保主分支常年处于良好状态。代码门禁实施起来也很容易,很多开源和商用的CI/CD平台都支持,例如GitLab+Jenkins。

只要做得好,代码门禁是不会降低工程师的日常效率的。“做得好”的标准是:

非常强大的跑回归的能力

有了强大的回归能力,就能在代码频繁的合并回master的情况下,仍然保持master分支处于可发布状态或者接近可发布的状态,有了强大的回归能力,我们甚至可以把一小部分的回归放到代码门禁里面去跑,那将会进一步有助于保持master分支处于可发布状态。

回归能力的强大体现在以下几方面:

这几方面的回归能力相互之间是相辅相成的,能够形成正循环,产生“飞轮效应”:

强大的回归能力的背后需要的支撑能力是:

把大项目拆成小项目做的能力

如前所述,把代码拆成小块分多次发布,的确是会增加开发的工作量的。有不少开发同学不理解为什么要这样做。增加了这些工作量,能让我们的研发模式更加敏捷。这个代价是值得付出的,这些额外的时间是值得花的。

大项目拆成小项目做的一些常见套路包括:

大项目拆成小项目,还需要有比较强的需求拆分的能力:能够把一个全链路级别的需求文档拆分成域级别、系统级别的需求,这样每个域、每个系统可以“分而治之”。

如何提高一个研发团队的“代码速度”?

Code Velocity和质量、线上稳定性的关系

从上面的分析可以看出来,提高code velocity并不是以牺牲质量为代价的。上面这些提高code velocity的手段,并没有cut corner,并没有降低质量标准,并没有比今天少执行任何测试。即便是频繁的把代码合回master,即便是把大项目拆成小项目做,该运行的各种验证和测试还是继续运行。而且,为了要提高code velocity,实行了代码门禁,建设了强大的跑回归的能力,反而是对质量有提高作用的。

提高code velocity也并不会降低线上稳定性。把大项目拆成小项目做、更加频繁的发布小块代码,能够降低单次发布的风险;发布中如果出了问题,因为payload小,排查和回滚也更方便。另外,在投入资源提高code velocity的同时,我们不会降低对故障发现能力、止血能力、应急能力、监控核对等能力的投入。提高code velocity不会导致线上技术风险防控体系变弱。

将来

如果一个团队的“能频繁的把代码合回master”的能力做得足够好了,就可以完全抛弃项目分支和迭代分支,每一个commit都直接checkin进master,而且master分支每天都有若干个可以发布的版本⁽⁶⁾,每个版本都可以用一个不同的release分支来保存。这就是所谓的“主干开发、分支发布”(Trunk-based Development)模式了。

到那时候,就有做到“天天发”的能力了。那时候,代码从commit到上线可能平均只需要两三天时间。那时候,因为有了“天天发”的能力,甚至连紧急发布都不怎么需要了。

如果你希望加入百度天津服务中心,可以随时与我们直接联系。爬虫、产品、Java开发、测试开发和工具开发等岗位虚位以待,有兴趣的童鞋可发简历至:

iamjasonchoi@qq.com

【注】

1.一般会有两个为期四周的迭代并行,每个迭代有自己的目标发布窗口。发布窗口一般是每两周一次。

2.“分支开发、主干发布”的开发模式来自于A successful Git branching model。但这种模式在实践中是有不少问题的(参见A succesful Git branching model considered harmful)。更好的模式是“主干开发、分支发布”(aka. Trunk-based Development)

3.主分支可以是master,也可以是项目分支或者迭代分支。

4.单元测试和接口测试看代码覆盖率,回归测试看业务覆盖率。这在行业内的一部分开发和测试之间已经形成共识了。

5.当然,我们可以用技术的手段使得分析500个失败的用例变得更容易。但这并不应该成为我们不去提高通过率的理由。

6.版本:对于“大库模式”(monolithic repo)来说就是一个commit,对于“小库模式”来说就是每个repo的一个commit构成的一个“截面”。

上一篇 下一篇

猜你喜欢

热点阅读