分支模式选型及落地指南

分支模式是我们在进行代码变更时的一种约定,它在版本管理工具(如Git)之上,约定我们在不同分支上的行为,达到提升开发协作效率的目的。

背景

让我们回到软件开发的早期,那时有很多独行的勇士,他们一个人写一个操作系统、一个周末搞一个编辑器。他们把代码上传到FTP上,或通过磁盘分发。这个时期软件开发由一两个人独自完成,不需要版本管理工具,也没有分支模式。

随着通用软件和行业软件的发展,软件的规模越来越大,有些软件需要几千名工程师,经过几年的开发、测试,才能最终发布。大家比较熟悉的windows、office软件等通信设备商开发的2G、3G基础软件是这方面的典型代表。随着大型软件开发的出现,版本管理工具随之出现了,如CVS、Subversion和clearcase。这一阶段通常是集中式的版本控制,分支比较昂贵,大家的提交频率较低,代码集成往往需要专门的配置管理工程师协助解决。软件发布的周期从数周到数年不等。

之后软件的应用场景越来越广,尤其是互联网应用的快速发展,要求软件的发布周期越来越短。于是,微服务架构被提了出来,单体应用根据领域被拆分成多个服务,每个服务可以独立开发、独立测试、独立部署。这样,很多需求只需要涉及少数的几个应用,在十几人以内的小团队内部就可以完成。互联网应用爆发,加上Git这样的分布式版本管理工具出现,多分支开发变得普遍,高效的开发者经常一天提交十几次。软件发布的周期缩短到数周乃至数天。

为什么要有分支模式

从前面的背景介绍,大家可能已经发现,分支模式是随着软件协作的需求和版本管理工具的发展而产生的,它的出现,本质上是为了解决两个问题:1.冲突;2.返工

通过避免冲突和减少返工,让开发者关注在需求开发上,提升软件的发布质量和发布效率。

当前,绝大多数软件研发都采用Git为版本管理工具,为了简单起见,之后我们提到分支模式,均特指基于Git的分支策略。

避免冲突

如果多个人同时在同一个应用的同一个分支上开发,那么他们两人的工作很容易产生冲突。这里的冲突不一定是Git提交时的conflict。像测试被其他人破坏,发布需要等待其他人完成,都属于冲突的范畴。简言之,一个需求的开发被其他需求开发活动所干扰,就产生了冲突。在Subversion时代,有些团队会采用定时排队提交,验证,回滚的策略,即到固定的时间点,按照协商的顺序,每个人按顺序提交自己的代码,然后由配置管理员进行构建、测试,如果测试不通过,则提交被回滚。在这个期间,其他待提交人只能等待。于是乎,每次发布前,配置管理员都会异常辛苦。而由于提交不易,工程师倾向于一次提交更多的东西,但提交的越多,产生冲突的可能性也越大。

另外一种冲突,是合并的冲突,如主干上的某个增强补丁合并到发布过的一个分支上,由于代码的基线不同,产生冲突,无法合并。

分支模式通过合理的开发、合并约定,来避免或减少上述冲突情况发生。

减少返工

由此可见,每一次返工的成本是很高的。

影响开发效率的另一个因素是返工。比如有些团队采用窗口制的发布策略,每周四是发布窗口。在这之前,很多功能都被提交到待发布分支上,如果此时针对待发布分支的测试发现某个功能有严重的问题,需要将该功能相关的提交从发布分支中去掉,不然就会影响其他功能的发布。那首先,得有人将该功能相关的提交都识别出来,进行回滚,重新进行构建和测试;然后,该功能在问题修复后,需要再次提交,并且祈祷不再出现问题。

定义分支模式

那什么是分支模式,它又包含哪些内容呢?分支模式的本质是需求交付的流程和约束策略,它通常包括:

  1. 定义分支的类型及其标识,通常包括:

    • 需求开发分支

    • 集成分支

    • 待发布分支

    • 发布分支

    • 热修复分支

  2. 分支的生命周期及创建消亡规则。

  3. 提交在分支间的流转规则和约束条件。

  4. 发布与代码的对应关系,如Tag等方面。

如:某团队有20名开发,为企业客户提供SaaS服务,定期发布版本。他们的分支模式如下:

br1

  1. 存在四种分支:feature分支、develop分支、master分支和hotfix分支。

    • feature-.*:需求开发分支

    • develop:集成分支和待发布分支

    • master:发布分支

    • hotfix-.*:热修复分支

  2. developmaster为长期分支,feature-.*hotfix-.*为临时分支,开始开发时从develop创建,完成后合并入develop,分支消亡。

  3. master不允许直接提交,必须从develop分支合并过来。

  4. 每次发布会在master上创建Tag,发布与Tag一一对应。

怎么选择分支模式

同时,如果有其他手段可以解决(如采用Feature Team减少跨团队协作),就不要用分支模式来解决。

在搜索引擎里搜索“分支模式”,大家可能会看到五花八门的各类分支模式,著名的如Git Flow、GitHub Flow、GitLab Flow和TBD(Trunk Based Development)等。怎么选择呢?

原则

建议是根据团队和应用的具体情况,按照越简单越好,越自动越好的原则来选择。

方法

具体落地的时候,分别考虑团队和应用的特点。

对于团队,需要考虑的有:

  • 团队数量

  • 团队规模

  • 团队内协作需求

  • 跨团队协作需求

  • 团队技术偏好

对于应用,需要考虑的有:

  • 构建依赖

  • 运行依赖

  • 发布依赖

  • 单应用规模

  • 总应用数

  • 单次发布应用数

然后根据开发->集成->验证->发布的流程

  1. 从右往左来确定每个阶段存在的冲突和协作需求。

  2. 判断是否需要在该阶段使用单独的分支。

  3. 确定分支的进入条件和生命周期。

有些团队在选择分支模式时有个误区,倾向于选择看起来更专业的复杂分支模式。例如一个不到10人的团队却选择Git Flow这样复杂的模式。而事实上在10名研发规模的创业公司采用类似下图的只有一个分支的非常简单的分支模式,在保证微服务拆分和自动化测试的前提下,效果很好。

br3

最后,一旦选择了某个分支模式,就要保证切实执行,并定期评审,确保现有分支模式符合当前研发现状需要。

怎么落地分支模式

我们以为什么要有分支模式一章中的模式为例,来看一下如何在云效Codeup中落地一个分支模式。

说明

立即体验:云效代码管理Codeup

1、配置保护分支和合并条件

develop和master分支不允许开发者直接提交,我们需要将他们配置为保护分支。打开对应代码库的设置页面,点击分支设置,新建保护分支规则(以master分支为例)。

打开合并前代码评审和自动化检查选项并进行配置。高的高的 (1)

2、配置流水线模板

接下来,我们在云效流水线Flow中,为feature-.*hotfix-.*developmaster分支分别创建不同的流水线模板:

  • 针对feature-.*分支的开发阶段流水线。

  • 针对hotfix-.*分支的紧急修复流水线。

  • 针对develop分支的集成阶段流水线。

  • 针对master分支的发布阶段流水线。

说明

立即体验:云效流水线Flow

高的 (2)

3、应用流水线模板

接下来就是在各个应用中使用这些流水线模板了。对于不同的应用类型(如Java应用和Golang应用),建议在阶段上应该是相同的,只是每个阶段的具体内容有所差别(如Maven单元测试和goconvey单元测试)。

至此,我们就可以在既定的分支模式的约定下进行开发和发布了。

实践建议

  • 单主干:一个代码仓库应该保证有且仅有一个主干分支。

  • 最少长期分支:在避免冲突的前提下,尽量减少长期分支的数量。

  • Promotion:代码的提交应该是逐级合并,如feature->develop->master,避免feature->develop和feature->master同时存在。

  • 发布不可变:发布的版本应该是不可变且可回溯的。

  • 自动化事件触发:分支的持续集成过程应该是自动化的,且通过提交事件或制品变更事件自动触发。