小小的代码分支模型如何撬动研发过程管理
背景
日常的迭代研发过程中,大家对于多团队、多功能并行开发最头疼的,就是版本的分支管理了。你可能常常遇到类似这样的几个场景:多个需求同步开发,准备上线前夕却发现某个需求存在重大技术或需求分析问题,无法上线,代码很难拆分。
底层架构需要进行重构,开发工作持续了若干个迭代,上线时发现和线上版本差异过大,发布合并过程异常艰难。研发过程中分为研发、测试、预发、正式等多个环境,如何管理不同环境的代码分支同时隔离变更影响?发现线上异常,怎么拉分支来修改,如何测试并完成上线?
可能针对分支管理的问题还有很多,我接下来会对这些问题,进行更细致地分析,并且做症结上的确认。大家再遇到分支管理的问题,相信都可以按图索骥,自己思考解决的方法了。
另外今天我不单提供给管理职能的同学,希望从中能够得到一些参考,形成自己的管理模式。还希望研发的同学,也能够了解并思考当前自己团队里正在用的分支管理模型。只有咱们真正理解为什么这么做,才能做得更好。
Git是一个开源的分布式版本控制系统,类似的产品还有subversion,Mercurial等。
Git在2005年发布了1.0版本之后,由于自身所带着的开源的血液,迅速在开源世界中流行起来,慢慢成为了软件研发团队的首选版本管理控制软件。
Git分为本地暂存区,本地仓库,远程仓库三部分存储空间,从本地到远程,每部分都可以保存变更而不影响到上层存储。
接下来我们就引出了今天管理模型涉及到的最重要的Git特性,分支(Branch)。有人说了,分支好像没什么特别的,之前几乎所有的版本控制都有这个概念呀,Git特殊在哪里呢?
我们以subversion举例,它的branch命令,实际底层都是使用svn copy生成的,所以是针对项目的完整拷贝。当项目比较大的时候过程是非常耗时的。
Git的分支命令则是非常轻量的,当我们尝试在一个正常的分支上进行branch命令时(例如我们新增一个test分支),实际上只是添加了一个指向提交的指针。所以无关项目大小与其他分支情况,分支都能很快速的建立。
在企业的研发过程中,我们应该如何利用Git分支这个概念呢?在企业的迭代研发过程中,我们常遇到下面的几个问题:
复杂度(Complexity)
企业都希望研发团队能够快投入生产,而在版本管理这种“小事”不要花费太多精力或者太难上手。
需求变更(Release)
企业在一个迭代过程中是否能够完全不进行需求的变更,无论你们的周期是两周、一个月,还是一季度。
线上修正(Hotfix)
系统目前是否已经非常稳定,不存在需要发布线上修正的可能。
多环境(CI/CD)
企业是否存在多环境的要求(测试、预发布、正式等)
长期需求(Long-Lived Version-control Story)
企业是否存在一个大需求可能要持续数个迭代。(例如底层的基础业务模型扩展,增加新的数据隔离标识字段,涉及到系统底层或者全系统业务逻辑的变更。这里列这种需求并不是说必须支持,有些普通需求由于错误的技术方案/市场的变化等情况就会变化成这样的需求,所以综合考虑边界情况。)
那分支管理能够帮我们什么忙呢?核心在于两个字:隔离。我们使用不同的分支,将团队和代码都隔离开,让不同的人员,针对同一份代码块,进行不同的特性开发。当然我们还要依赖Git的merge与rebase等命令,最终能够完美地将这些分支合并起来。
有了分支,我们就可以考虑根据企业的实际情况,何时拆分分支,何时合并,能够满足我们复杂的并行研发和发布的需要。当然了业界现在也有了很多的模型提供给我们参考,我们挑一些常见或者比较知名的模型来进行分析,最终学习如何结合我们的实际情况挑选或者修改模型,来符合我们自己的研发模式。
TrunkBased模型
Trunk在subversion中是三个核心概念之一(另外两个是branch和tag),是主干分支的概念。TrunkBased模型也来源于此。
模型原理/思想:研发团队在名称为 Trunk 的单一分支进行开发,当开发工作到一定阶段的时候达到可发布条件后,切出Release分支进行发布,并且Release分支是不可以修改的仅仅做发布使用。
模型操作过程:
分支仅保持master,日常开发也是直接在master上进行(当并行团队特别多时可以考虑拉出短期feature分支,开发完成后合并回master)。
问题维度以及对应支持情况
复杂度:简单
需求变更:不支持
线上修正:不支持
多环境:不支持
长期需求:不支持
最大的优点是简单,缺点就是太简单,从而无法支持稍微复杂的场景。
可以看到本模型甚至没有用到太多Git本身的特性(如tag等),适合从subversion刚刚切换到git的团队做一个适应性的过程模型。
模型使用团队/场景:
但是这么简单的模型,仍然有实际的适用场景。
适合于单一稳定产品线,迭代排期稳定,需求边界完全可控的小规模团队。或者进入到后期维护的大型系统,不会做太多的变更并且不会有太多的突发问题。
因为涉及的人少、需求稳定的情况下,最重要目标的是简单、安全稳定。
GitFlow模型
GitFlow来源应该是 Vincent Driessen 在2010年1月发表的这篇《A successful Git branching model》,基本是现在Git中最出名的流程管理方法了。
模型原理/思想:
主要分为5个分支,其中master和develop为固定(长期)分支,始终存在不会删除。feature、release和hotfix为临时(短期)分支,按需拉取,使用完成后删除。
master分支存放所有正式发布的版本,可以作为项目历史版本记录分支,不直接提交代码。仅用于保持一个对应线上运行代码的 code base。
develop分支为主开发分支,一般不直接提交代码
feature分支为新功能分支,feature分支都是基于develop创建的,开发完成后会合并到develop分支上,同时存在多个。
release分支基于最新develop分支创建,当新功能足够发布一个新版本(或者接近新版本发布的截止日期),从develop分支创建一个release分支作为新版本的起点,用于测试,所有的测试bug在这个分支改。测试完成后合并到master并打上版本号,同时也合并到develop,更新最新开发分支。(一旦打了release分支之后不要从develop分支上合并新的改动到release分支),同 。
hotfix分支基于master分支创建,对线上版本的bug进行修复,完成后直接合并到master分支和develop分支,如果当前还有新功能release分支,也同步到release分支上。同一时间只有1个,生命周期较短。
模型操作过程:
功能迭代开发时:
从master拉出develop分支
从develop拉出feature分支进行开发
开发完成后合并回develop(合并后可删除feature分支)
从develop拉出release分支进行测试以及bug修正,修正的内容合并回develop
测试完成后合并至master分支,同时打上tag标记版本(合并后可删除release分支)
线上出现问题时:
从master拉出hotfix分支
测试完成后同时合并至master和develop分支,同时打上tag标记版本(合并后可删除hotfix分支)
问题维度以及对应支持情况
复杂度:相对复杂
需求变更:不支持
线上修正:支持
多环境:支持
长期需求:支持较差
模型多环境对应:
master对应生产环境,用于在任何时候发布生产。
develop对应研发环境,用于研发联调与冲突解决。
release(多)对应测试/预发布环境。
feature(多)单纯用于开发。
hotfix(多)对应线上修正开发。
模型使用团队/场景:
GitFlow已经很偏向互联网风格的代码管理,考虑到了多环境、线上修正。而且目前很多IDE有整合了GitFlow的插件,团队内使用推广也会更加简单。
两条主干分支,master对应了线上的实际版本,develop对应了研发的实际版本,所以两个分支之间由于时间差可能会存在需求变更的情况(如feature1、feature2已经提交到develop,但是feature2需要提前发布),这种复杂情况就会无法满足。
所以团队规模大小理论上都是可以用的,迭代的周期较短(如1-2周)需求相对稳定的项目会更加合适用这样的模型进行分支管理。如果需求的发布版本无法明确、发布内容变更可能性较大、或者测试跨多迭代的长期需求,则使用起来可能问题会多一些。
Aoneflow模型
阿里的研发效能事业部专家基于TrunkBased和GitFlow提出了一套新思路:AoneFlow。
模型原理/思想:
只使用三种分支类型:主干分支(master)、特性/修正分支(feature/hotfix)、发布分支(release),以及三条基本规则。
模型操作过程:
功能迭代开发时:
开始工作前,从主干创建特性分支
通过合并特性分支,形成发布分支(如果是单个特性分支需要发布也可以考虑直接作为发布分支使用)
发布到线上正式环境后,合并相应的发布分支到主干,在主干添加标签,同时删除该发布分支关联的特性分支
问题维度以及对应支持情况
复杂度:相对复杂
需求变更:支持
线上修正:支持
多环境:支持
长期需求:支持
复杂度上需要维护的分支减少了develop分支,这样也导致了多环境的支持相对复杂,原本可以基于develop作为开发联调环境,现在特性分支就需要能够进行联调会更多更复杂(如果基于容器会简单一些)。
模型多环境对应:
master对应生产环境,用于在任何时候发布生产。
release(多)对应测试/预发布环境。
feature(多)对应研发环境,用于研发和联调。
hotfix(多)对应线上修正开发。
模型使用团队/场景:
AoneFlow的模型不算简单,非阿里团队如果想要实施需要自行对分支进行管理,这样会造成复杂度稍高。而阿里基于云效平台屏蔽了大量细节,研发人员只需要维护特性分支,所有的后续合并发布等操作都基于平台的流程管控。
适用的团队规模同样也是大小都可以,是能够很好的拥抱需求变化与长期需求的支持。有实施容器化的团队会在环境隔离(CI)上更加轻松,如果并行需求较少可以固定维护一到两个环境用于研发联调。
MyFlow模型
MyFlow的意思,实际上是“我的”模型,这里会以一家SAAS平台的中小企业为例,我们一起来实际的分析一下如果选择和打造适合自己团队的Flow模型。
企业背景:
这是一家面向B端的Z企业内部培训SAAS平台,平台整体都是托管在阿里云。
产品的形态用户端是APP(原生+Cordova混合开发),后端包含企业管理后台和租户运营平台。
服务的对象包括安踏、建发、顺丰等大中型客户,会涉及定制化需求,定制需求的上线效率与完成度也是客户服务的要点之一。
团队分为前端、原生、服务端。各端都是有比较资深的TL带领,有丰富的行业与团队管理经验。
迭代内容主要包括客户的定制二次开发,产品的更新以及一些优化。迭代基本以月为周期,一般是3周左右的开发时间,1周测试。产品在不断演进的过程中,随时可能有一些线上的修正需要发布。后端是微服务(大约30个服务)但是目前还没有容器化部署,环境隔离区分了研发、测试、预发+正式,三套环境。考虑到成本,研发和测试环境都只部署了一套。
企业痛点:
有一些长期的需求,可能涉及到底层架构的优化,不仅仅开发时间长,测试时间也很长。
修正内容很多是客户发现一般都是需要尽快修复测试验证后紧急发布。
迭代开始初期安排的计划,有可能出现需求变更、研发延期、插入内容导致无法在预期时间节点正常发布。企业对于
复杂度(Complexity)
需求变更(Release)
线上修正(Hotfix)
多环境(CI/CD)
长期需求(Long-Lived Version-control Story)
五项中的后四项都有强烈的需求,复杂度上能够接受比较复杂的模型。
模型选型分析:
不考虑复杂度,所以我们基本可以选择满足需求的任何模型,GitFlow自然是首选,在实施一段时间后发现存在的问题就是某些需求可能以及开发完成并且进入测试,但是由于客户要求暂时无法上线却也无法从已经合并的分支中移除。接着考察了AoneFlow,发现相对比较灵活,但是研发联调无法针对每个分支都提供单独的环境。研究讨论之后,融合了这些模型结合公司自身的特点整理出了一套适合我们自己的模型体系。
分支的划分和GitFlow类似,包含五个分支:
master
develop
feature
release
hotfix
其中master、develop、release是长期分支,其他都是临时分支
模型操作过程:
功能迭代开发时:
第一次基于master拉出develop分支
根据迭代确定的需求基于master拉出feature分支
需要联调或者测试时,将feature的代码合并至develop,并且后续的修改都是在feature上进行,修改结束同步到develop分支
测试完成后需要进行预发布验证,这时候根据能够发布的内容合并出release分支
合并到master,同时打版本tag
develop基于master进行rebase
线上出现问题时:
从master拉出hotfix分支
测试完成后同时合并至master分支,同时打上tag标记版本(合并后可删除hotfix分支)
问题维度以及对应支持情况
复杂度:复杂
需求变更:支持
线上修正:支持
多环境:支持
长期需求:支持
分支数量较多,而且在各个环境变更时,都是从feature分支修改,同步到多个分支操作较多。整体来讲复杂度较高。但是对于其他的需求都是能够比较好的支持。
这里还有一个缺点,从图上可以看出develop分支实际上是一个单向分支(只有输入没有输出),长期之后会造成develop和master之前的差异越来越大,测试验证的可靠性也会逐步降低,所以团队还要考虑develop是否需要定期整理。
模型多环境对应:
master对应生产环境,用于在任何时候发布生产。
release对应预发布环境。
develop对应研发联调/测试环境,用于研发联调与测试。
hotfix(多)对应线上修正开发。
feature(多)对应纯研发。
针对MyFlow的说明基本就是这样,在Z企业实施过程中满足了研发和产品的需要。
今天的分享基本上就到这里,最后再来总结一下。
我们首先介绍了三种Git模型,分别是:TrunkBased、GitFlow、AoneFlow。
模型选型原则整理:
复杂度:尽量减少分支种类就能大大降低复杂度。
环境隔离:团队需要隔离出哪些环境,正常就需要提供这些分支,如果环境之间的代码范围没有变更则考虑可以合并分支为同一个。例如迭代过程中,研发、测试、预发布分支尽量不同,但是例如测试和预发布的范围完全相同,则可以考虑不用多划分出一个分支。
需求变更:如果存在需求变更的可能,或者说发布时间比需求的范围更重要,特性分支就是越晚合并越好。
长期需求:长期研发、长期测试的都是相当复杂的情况,最好是有独立的环境,并且需要不断和master分支进行合并以保持不会过于滞后。
评论