短迭代的分支开发管理流程,听听鹅厂高级工程师陈争云怎么说

我们团队承担着多个面向客户的业务系统、工具的开发维护任务,它们按照技术特点分为

- 前端/终端
- web应用
- Service/API
- 离线应用

等类型。很多系统业务庞杂,规则多变,多人并行协作开发。时时伴有多版本并行、版本间发布超车,还有紧急线上问题修正和临时版本发布等现象。针对这些特点,经过多种开发模式的实践,我们归纳总结出一种基于迭代的分支开发模式,并在团队内推广开来。

遇到过的问题

Case 1:A系统有时候后一个需求在前一个需求的基础上开发。前一个需求不回trunk,后一个就没法拉分支。  如果合并为一个分支又会拉长开发时间。而且代码多了回trunk也挺费劲,需要仔细review。 

Case 2:B系统在分支开发,每周从trunk合并一次代码,但有时遇到冲突问题,会投入不少精力解决。 

Case 3: C系统是分支开发,主干提测,当多个需求并行,且代码从不同分支merge到主干后,临时遇到某一个需求的线上外部依赖不满足,导致其它需求也被阻塞无法提测。

当前的解决之道

Case 1:我们的迭代周期是一周,每个迭代分支的生命周期较短,代码不会长久跟主干脱节。同时规划环节会把相关需求排在同一迭代。 

Case 2:工具自动每天merge,避免手工工作,减少合并冲突次数,降低难度 

Case 3:分支merge回主干前会最后一次从主干同步,并有工具自动diff,一旦决定merge就会马上在主干发布,留下的串行排队冲突时间窗口极短(可忽略)

设计哲学

分支管理的策略确实很考验对人性的理解,从理念上说它要

- 符合人性
- 能自动化的就不要人工做,缺少工具支持的流程就是耍流氓 

要让它既灵活,又简单,就需要

  1. 首先别让我记住很多规则
  2. 其次别让我做很多合并
  3. 最后是敏捷,如何让我不要被别人耽搁 

所以:

  1. trunk 发布是最简单的规则,任何时候不管是家里,公司,新人旧人都可以记住
  2. 迭代分支直接开发,免去特性分支到迭代分支的合并,简洁易行
  3. 提前做好迭代的规划,迭代分支已经是比较不可分割的整体,全体特性一起上或一起延迟,敏捷是有限度的

基于迭代的分支开发

我们研发内部,最终提出了一种统一的分支开发流程:

分支开发-分支测试-分支merge和回归-主干发布

  


 它是基于以下目的:

- 支持多版本并行开发,特性不被隐式夹带
- 支持超车发布和延时、取消发布
- 永远保持主干的release-ready
- 保持全流程的简单性 

商务中心有多种测试发布工具,我们希望做到工具间流程的打通,再辅以新的自动化环节,帮助我们一起做好微服务下的持续发布,打造成功一个大的、内聚的开发测试发布工具集平台。目前这部分得到业务支持中心测试团队同事的大力支持,他们负责这部分的功能开发、工具部署,其中许多都已经开发完毕并投入使用。

 这里给出我们总的流程图:


 

流程

描述

责任人/责任方

1、Tapd提单

产品/技术需求提tapd单,排期,定迭代(bug fix暂不走此流程)

产品/模块owner/PM

2、创建迭代分支

当某项目的需求在TAPD建立后,关联到一个迭代(固定格式命名),测试工具会自动发现新迭代并创建SVN的迭代分支(以迭代号建立一个目录,项目分支放入该目录)。迭代分支将实现代码审核、持续集成等严格流程保证可发布性

工具

3、代码merge和开发

工具自动完成从主干到分支的代码merge,开发人员完成代码开发

开发/工具

4、自动化测试

工具每日回归测试,单元测试

工具

5、CR与代码提交

发出CR并提交代码(要有tapd story id),然后扭转tapd单子状态为待测试

开发

6、覆盖率检查

工具提供项目总体覆盖率、本次提交增量覆盖率的指标计算

开发/工具

7、部署测试环境

工具收到待测试状态信号后,自动打包,建测试环境(某些工具做不到的部分可以人工介入)

工具

8、功能测试

测试人员进行功能测试(人工测试)

测试人员

9、对比变化并发布或重新回归

迭代分支生命终点有一个decision point:是否merge到trunk?一旦决定merge,并且merge成功,必须进入发布状态,不可回退;有任何变化则merge失败,重新ut和回归测试

开发/工具

 我们看一下相较于其它分支模式,它在解决什么问题。

与其它模式的对比

模式

开发

提测

回归

发布

A

分支

分支

分支

主干

B

分支

分支

主干

主干

C

分支

主干

主干

主干

D

分支

分支

分支

分支

E

特性分支

发布分支

发布分支

发布分支

 解释一下特性分支与发布分支:

- 特性分支:特性分支是指为一个特定的需求/任务/缺陷创建的分支,在其上完成相应开发后,会把它合并到发布分支

- 发布分支:为了集成代码并最终发布版本而创建的分支

vs 主干提测(模式C)

主张主干提测的,是希望保证“提测什么就发布什么”原则,这是一个很强有力的理由,但它不能保证主干发布前没有其它不干净的代码,此时需要临时决定是否要将其一起发布。如果评估决定不行,那么主干代码已经无退路,势必影响该次迭代的发布。 

那么分支提测则给了大家做决定的缓冲期。分支代码每日与主干同步,在一天内,分支owner有足够机会评估了解主干代码的新变化结果。它成为经过评估后的夹带,可以保证安全。

vs 分支发布(模式D)

既然前面说到的主干发布对于互相夹带的不可避免,那为什么不采用分支发布呢?一个项目同一天有两个人同时发布,代码如果都没合到trunk,直接分支发布,这样就会相互踩踏,甚至导致严重的线上事故。

vs 主干回归(模式B、C)

不少项目把代码merge到主干后在主干进行回归测试,其隐含好处就是测试什么发布什么。这个在前面小节中已经讨论过了。除此外,他还有一个略不完美之处是不能保持主干随时处于release-ready状态,可能由于集成后导致主干的持续集成build failure。

vs 多个特性分支,一个发布分支(模式E)

这个思路其实是GitFlow的做法,它有多个特性分支,还增加一个新的发布分支。提测时,不再是将所有待测试的分支直接合并到trunk,而是从trunk拉出一个发布用的分支,把所有的特性分支合并到上面,测试完成之后从改分支发布,然后代码合并回trunk。同时trunk只接受发布分支的代码,不允许通过其他入口往里提交。 

 


它解决的是业务特性临时取消或延时发布,一些项目会遇到比如某个需求都测试通过了,去做线上验证了,发现线上还是有些东西没有ready,得取消发布;或者是因为市场推广原因,想延迟上线等等。实现的手段就是重新拉一个发布分支,摘除要取消或延迟的特性分支,这样其它的特性还是可以正常发布。等发布完成后,再做主干代码的merge。这时合并回到trunk的时候并不会发生冲突,它是一次fast forward式的合并,也保证了主干代码的随时可发布。 

不得不说,我们前面定下的分支开发模式,并不能很好的支持这个场景,因为要保持流程的相对简易,就要牺牲面面俱到。GitFlow用一套比较复杂的多分支、繁琐的合并规则来实现了这点,而暂时我们还不想去做的那么复杂。回过头,我们更希望在迭代需求的计划环节定义好哪些特性可发布,在早期的依赖诊断环节去思考潜在的问题。而且即使真正在线上验证出问题,或者是需求临时取消的状况下,我们分支回归、主干发布有一个decision point,可以提供缓冲,让发布者有时间来思考是否要merge代码回trunk,是否要马上发布?这依然是一个不算坏的替代方案。

主干开发

 


主干开发在这里的含义如上图,它有如下特征:

  1. 所有人工作在单一的主干上
  2. Bug fix也在主干进行
  3. 没有代码会被冻结
  4. 没有merge灾难
  5. 不会build broken,它永远是release-ready状态 

对于极小型的、业务功能不大变化的Service/API项目,或者是线上bug紧急fix,我们依然使用主干开发。它是最简单、最可控的一种。

其它开发规范的支持

商务中心有比较严谨的开发规范,这是支持分支开发模式的一些“软力量”。 

例如我们主干代码的提交必须通过Code Review,对主干的build broken必须立即处理。 

在我们的项目依赖上来说,主体上是jar包依赖,而且还搭建了内部的maven私服来加速依赖包的下载上传。并在内部的公共脚本中规定,release版本不可以依赖snapshot包,这样保证jar包依赖打出来的发布产物是一致、稳定的。同时新项目在须尽可能使用最新的jar包。 

在相同项目内,有时采用源码依赖,分割成不同小项目,这于是也保证了开发环节的高效率。

展望

我们采用的“分支开发-分支测试-分支merge和回归-主干发布”分支开发模式,还不能解决特性分开发布(或临时摘除)的问题,但我们强调在迭代规划阶段把待发布特性的前后影响尽可能想周全,避免仓促变化。另外,我们也打算后续在SVN用cherry-pick模式来解决需求分开发布的问题。