分支策略 — 软件协作开发的灵魂
作为程序员,你是否有过这样的经历?软件发版前,你需要把你的代码合并到主分支,拉取主分支代码后,发现有数十个文件冲突,你开始小心翼翼地解决冲突,有些其实你也不知道咋处理,几番确认后,最后终于提交上去了。之后测试人员对主干代码进行测试,还是发现了一个之前别人已经解决过的Bug,之后别人就找你了,质问你为什么把他的代码合丢了。。。
最要命的是,这种事情每隔一两个月就会发生一次,每次弄得大家都很痛苦,有时着急发版本,但是因为代码中的大量冲突却总是拖后腿,甚至遇到大版本发布,解决冲突就要耗费长达一两天的时间,团队也因此加班,并出现版本发布Delay。
有时,我们会想,有没有好方法能解决这个问题,要是不出现那么多代码冲突就好了。
当然有,如果我们把各个服务对应的代码仓库或者模块拆分得比较细,每个人单独负责一两个代码仓库或者模块,每位程序员在一个版本周期内,只会修改自己单独负责的代码,这样就不冲突了嘛。
是的,模块职责拆分的越清晰,版本周期越短(比如一周一个版本),冲突的概率就会越小,上面的问题也越容易解决。但是,我们无法避免一份代码库由多个人维护,尤其是现在大仓很流行,被视为卓越工程水平的体现,版本周期需要尽可能短,但是项目制推进的大版本,或者研发团队很多人的时候,代码合并时冲突的概率还是很大。
其实,不只是代码合并冲突问题,线上问题如何修复,新功能开发时团队如何选择分支,这就需要我们选择一个适合团队的分支策略来解决。只有明确了这些规则,团队才能基于共同的代码仓库来高效工作。
分支策略一般会规定基于哪个分支进行需求迭代、基于哪个分支来发布版本;如果线上出现问题,基于哪个分支进行修复,修复后如何同步该修复到相关分支;不同的部署环境采取什么样的分支策略等等。分支策略是多人软件协作开发的灵魂,基于此可以构建代码提交、审核、集成、测试、发布等工作流程,进而形成优秀的软件协作模式和发布模式,选择一个适合团队的分支策略对于DevOps的实践落地大有裨益。
总之,分支策略不仅能够帮助我们解决代码合并冲突,更能够规避因此带来的质量、效率问题。实质上,分支策略关乎产品、研发、测试和运维人员的整个研发协作流程,好的分支策略可以帮助我们建立产品协作开发的规范,提升软件交付效率,让研发人员更多的时间投入需求开发等真正创造价值的活动中,而不是解决代码冲突,也让我们可以清楚了解到代码改动关联什么需求、当前的哪个代码版本是稳定可靠的,增强团队的交付信心。
下一篇,我们将介绍几种常见的分支策略,并对比他们的优缺点,敬请期待!
DevOps提倡的主干开发,真的适合我所在的团队吗?
上次我们提到分支策略是软件协作开发的灵魂,今天我们主要聊一聊常见的分支策略,以及如何选择的话题。
分支策略大致可以分为主干开发和特性分支开发,主干开发包括分支发布和主干发布两种,而我们熟知的Git Flow、GitHub Flow以及GitLab Flow都是属于特性分支开发。如下图所示:

++01 主干开发++
在主干开发模式中,团队将所有的工作直接在主分支(通常命名为 master 或 trunk)上完成。主干发布的核心理念是快速、频繁地将代码变更合并至主分支,确保主分支始终处于可随时发布的状态。这种模式尤其适用于那些采用持续集成与持续部署(CI/CD)实践的敏捷团队,通过自动化测试和部署流水线,实现高效且低风险的版本迭代。
在主干发布的场景下,当线上出现紧急问题时,开发者可以直接在主分支上进行热修复,修复完成后立即部署上线。但这也要求团队具备较高的自动化测试覆盖率和高效的代码审查机制,以保证主分支代码的质量和稳定性。
主干开发又可以分为分支发布和主干发布两种。
++主干开发、分支发布++
采用主干开发,一般在发布时会拉取发布分支,问题修复先提交到 trunk 分支,之后再合并到release。如下图所示^[1]^:

++主干开发、主干发布++
更彻底的做法是,直接在 trunk 分支发布,这通常需要完善的流程、工具链和高质量的自动化测试保障质量。GitHub、Etsy、Netflix等公司都是采用这种方式。

++02 特性分支开发++
++Git Flow++
从Develop分支拉取一条Feature分支,开发团队在Feature分支上进行新功能开发;开发完成后,将Feature分支合入到Develop分支,并进行开发环境的验证;开发环境验证完成,从Develop分支拉取一条Release分支,到测试环境进行SIT/UAT测试;测试无问题后,可将Develop分支合入Master分支,待发版时,直接将Master分支代码部署到生产环境。如下图所示^[2]^:

Git Flow的优点是每个分支都有明确的定义,严格按照GitFlow管理项目代码的话,很难出现代码混乱;其缺点是:如果特性分支过多的话很容易造成代码冲突,从而提高了合入的成本;由于每次提交都涉及多个分支,所以Git Flow也不太适合提交频率较高的项目。
++GitHub Flow++
GitHub Flow 来源于GitHub团队的工作实践,这个模型中,通常只有一个Master分支是固定的,而且GitHubFlow中的Master分支通常是受保护的,只有特定权限的人才可以向Master分支合入代码。
在GitHub Flow中,新功能开发或修复Bug需要从Master分支拉取一个新分支,在这个新分支上进行代码提交;功能开发完成,开发者创建Pull Request(简称PR),通知源仓库开发者进行代码修改review,确认无误后,将由源仓库开发人员将代码合入Master分支。
很多人可能会问,提交代码通常是commit或者push,拉取代码才是pull,为什么GitHubFlow中提交代码提出的是“Pull Request”。因为在GitHubFlow中,PR是通知其他人员到你的代码库去拉取代码至本地,然后由他们进行最终的提交,所以用“pull”而非“push”^[5]^。大致流程如如下图所示^[4]^:

GitHubFlow优点是相对于GitFlow来说比较简单,其缺点是因为只有一条Master分支,万一代码合入后,由于某些因素Master分支不能立刻发布,就可能会影响发布。另外,每个特性都Fork代码仓库对于企业来说也过于繁琐,这种方式更适合开源项目。
++GitLab Flow++
GitLabFlow出现的最晚,GitLabFlow是开源工具GitLab推荐的做法^[6]^。GitLabFlow支持GitFlow的分支策略,也支持GitHubFlow的“Pull Request”(在GitLabFlow中被称为“Merge Request”)。
相比于GitHub Flow,GitLabFlow增加了对预生产环境和生产环境的管理,即Master分支对应为开发环境的分支,预生产和生产环境由其他分支(如Pre-Production、Production)进行管理。在这种情况下,Master分支是Pre-Production分支的上游,Pre-Production是Production分支的上游;GitLabFlow规定代码必须从上游向下游发展,即新功能或修复Bug时,特性分支的代码测试无误后,必须先合入Master分支,然后才能由Master分支向Pre-Production环境合入,最后由Pre-Production合入到Production。具体如下图所示^[4]^:

GitLab Flow中的Merge Request是将一个分支合入到另一个分支的请求,通过Merge Request可以对比合入分支和被合入分支的差异,也可以做代码的Review。
需要发布版本时,GitLab Flow提倡先创建一个stable分支,之后修复bug先合并到Master分支,之后再合入到stable分支。如下图所示^[6]^:

++特性分支组合发布++
阿里云效的分支模式如下图所示^[7]^。基于 master 分支最新版本创建 feature 分支。然后在 feature 分支上开发、测试,直到这个 feature 功能完成,质量 OK,准备好去集成和发布。release 分支用于集成和发布。基于 master 分支最新版本创建一条 release 分支,然后把想要集成的各条feature分支合并到这条release分支,进行部署和测试工作。当 release 分支上的质量足够好,本次想上线的功能也都具备之后,就要考虑发布上线的问题啦。如前面讲的,发布上线前,会确保它基于基础分支(常见的如 master )最新。而发布后会把 release 分支合并回 master,让 master 代表最新发布版本。

DevOps倡导主干开发^[8]^,然后主干开发也对开发人员提出了很高的要求,个人觉得真正落地还是比较难的。我们对比下各个分支模式的优缺点,根据优缺点,大家可自行根据团队情况选取,也可结合不同分支模式使用。
| 分支策略 | 优点 | 缺点 |
|---|---|---|
| 主干开发、主干发布 | 简单,可以进行极致的CI/CD | 对团队开发人员要求过高; 需要保证提交原子性; 需要强大的CI/CD工具支持。 |
| 主干开发、分支发布 | 简单,要求主干质量相对降低; 也更适合客户端,比如App发版等,使用单独分支更容易回溯。 | 对团队开发人员要求高; 需要保证提交原子性; 需要CI/CD工具支持。 |
| Git Flow | 分支区分清晰,流程严谨 | 复杂,分支多 学习成本高; 合并费时 |
| GitHub Flow | 适合开源,Master管控严格 | 企业使用过于繁琐;合并费时 |
| GitLab Flow | 分支区分清晰,流程严谨 | 复杂,多环境分支必要性不强 |
| 分支开发+主干发布(推荐组合) | 团队能上手,对开发人员要求不高; 复杂度可控,主干发布减少了release等分支的存在,复杂度降低。 | 相比于主干开发,CI/CD效率低 |
我们再介绍分支开发+主干发布的组合具体如何操作!
代码合并冲突不断通宵解决,你的团队应该引入这种代码分支策略!
产品临近上线,你的缺陷单终于修复完了,一拉取代码,我X,冲突文件有20多个,完犊子,你知道这次又不能善了了。。。
一通操作猛如虎,你怒解决15个文件的冲突,还有几个确实不确定咋解决了,去隔壁的A团队逮住A同学逐行确认了一遍,又去隔壁的B团队找B同学确认了另外一个文件,你又发现C文件的修改完全不兼容,把两个同学都叫到一起讨论了怎么修改。。。
终于都解决了,你解决了所有冲突,代码提交到主干做集成测试验证。测试同学很快找到了你,问你为什么之前修复过的问题现在又出现了,或者她也不知道其实之前出现过,你一通定位,小半天过去了,发现是代码合丢了,顿时心里翻江倒海了一番。。。
以上描述是笔者两三年前真实发生的上线前代码合并的经历,当时在想,上个线真的是太痛苦了。如果当时有系统学习过代码分支策略,就会发现问题根源所在主要有两点:
1、多团队共用一套代码仓,同时同时负责维护同一个模块,同时开发造成大量冲突;
2、没有完善的分支开发策略和团队纪律,比如特性分支、小批次提交、频繁同步主干代码等。
上篇文章DevOps提倡的主干开发,真的适合我所在的团队吗?中,我们介绍了多种常见的分支策略,如果推荐一种普适性比较高的分支策略的话,我建议是:特性分支开发+主干发布。
++特性分支开发+主干发布的分支策略++
基本流程如下:
1、A同学要开始需求 f1 的开发,从代码仓库A的 dev分支 中检出 feature-f1 分支,开始开发;B同学负责相关需求 f2,也从代码仓库A dev分支 中检出 feature-f2 分支;C同学是需求 f1 的下游,由于公司采用的微服务架构,并且独立代码仓库,所以从代码仓库 C dev 分支中检出 feature-f1 分支。
2、A、B、C三位同学都有良好的持续集成意识,每天都从dev拉取最新代码,由于拉取频次高,且一个代码仓库基本上只有2、3位同学负责,所以冲突很少。
3、A、B、C三位同学完成单元测试、分支测试后,把代码提交Code Review,Review通过后,按需提交测试人员进行分支测试,之后代码合并到dev分支。
4、合入到dev分支后,提交测试人员进行测试,如果发现Bug,就在各自的特性分支修改验证通过后,再合入到dev分支。
5、dev分支验证通过后,代码合入master分支,master分支作为稳定分支,时刻都能进行发布,之后打Tag,部署到预发布环境中验证。如果预发布环境中验证出现问题,就还把修改提交到特性分支,之后dev验证,通过后再合入master,出新Tag,部署到预发布环境中再次验证。
6、预发布环境验证通过后,使用验证过的Tag,灰度发布到生产环境,之后全量部署到生产环境。
7、如果生产环境发布过程中发现问题,或者线上出现紧急严重Bug,可以从Master分支或者生产部署的tag检出Hotfix分支,之后部署到预发布环境进行验证,验证通过后部署到生产,确认已修复,再把本次提交合并到master和dev分支。
8、如果生产环境发现非紧急问题需要修复,当新特性/缺陷处理,从dev分支检出feature-xxx分支,之后按照上述流程上线发布即可。
上述流程如下图所示:
++分支相关的团队纪律++
只有分支策略还不够,分支策略一定要结合团队纪律来使用,包括如下几个方面:
1、短分支:特性分支的存活时间要短,一般认为不要超过3天,最长不超过1周。
2、尽早集成:尽可能早地集成到主干分支。
3、微服务:微服务切分也是减少冲突的重要基础,一人一个服务或者一人一个模块,肯定不会冲突。
4、合入前充分测试:尽可能保障dev分支的代码质量,master分支更是要保障随时都能分布,发现有Bug应第一时间修复。
++契合分支策略的版本发布节奏++
版本发布模块可大致分为项目制发布模式、城际快线模式和随时发布三种。
1、项目制发布模式
项目制发布模式适用于大型项目或重大功能升级,这类项目通常具有明确的里程碑节点、较长的研发周期和较高的协同要求。在这一模式下,版本发布节奏通常被规划为若干个阶段,每个阶段围绕特定的项目目标进行开发、测试和整合。将项目划分为需求分析、设计、编码、集成测试、系统测试、用户验收测试(UAT)和正式发布等多个阶段。
2、城际快线模式
城际快线模式适用于需求迭代迅速、发布周期较短的敏捷开发环境,如互联网产品或持续优化的服务。这种模式强调高频次、小步快跑的版本更新,以快速响应市场变化和用户反馈。
设定固定或灵活的发布周期(如每周、每两周),每个周期内完成若干个小型特性或改进的开发、测试和发布。特性如果赶不上这次发布周期,那就跟车下次发布。
3.、随时发布模式
随时发布模式是最为灵活的一种发布节奏,适用于高度成熟、自动化程度极高、具备完善监控和回滚机制的系统。在这种模式下,团队可以在满足特定质量标准的前提下,随时将代码变更推送至生产环境。
即时开发与验证:开发人员在
dev分支上完成特性开发后,立即发起Code Review和自动化测试。通过测试的代码变更几乎无缝地合并至master分支。实时监控与反馈:依赖强大的监控系统实时监测生产环境的各项指标,任何影响用户体验或系统稳定性的变化都能被迅速识别并反馈给开发团队。
滚动更新与灰度发布:采用滚动更新或灰度发布策略,新版本代码在部分用户或服务器上逐步部署,确保问题能在影响范围较小的情况下得到及时发现和修复。
快速修复与恢复:一旦发现线上问题,立即在
master分支创建Hotfix分支进行修复,修复后迅速回滚至生产环境,并将修复合并回dev分支,确保所有环境的一致性。
++结语++
项目制发布模式、城际快线模式和随时发布模式分别适应于不同规模、不同复杂度、不同发布节奏的软件开发项目。选择合适的版本发布节奏,并与特性分支开发+主干发布的分支策略紧密结合,同时注重团队纪律,能够有效提升团队协作效率,确保代码质量,降低发布风险,助力团队实现高效、稳定的软件交付。
++参考:++
++[1]++ https://paulhammant.com/2013/12/04/what_is_your_branching_model/
++[2]++ https://bbs.huaweicloud.com/blogs/281789
++[3]++ https://docs.github.com/en/get-started/using-github/github-flow
++[4]++ https://www.ruanyifeng.com/blog/2015/12/git-workflow.html?bsh_bid=2219775734
++[5]++ https://bbs.huaweicloud.com/blogs/281789
++[6]++ https://docs.gitlab.cn/jh/topics/gitlab_flow.html
++[7]++ https://help.aliyun.com/document_detail/224587.html
++[8]++ https://dora.dev/devops-capabilities/technical/trunk-based-development/