持续交付之如何选型代码分支策略

zuozewei 发表于 2021/09/25 14:35:55 2021/09/25
【摘要】 高效的持续交付体系,必定需要一个合适的代码分支策略。采用不同的代码分支策略,意味着实施不同的代码集成与发布流程,这会影响整个研发团队每日的协作方式,因此研发团队通常需要很认真地选择自己的策略。

前言

高效的持续交付体系,必定需要一个合适的代码分支策略。采用不同的代码分支策略,意味着实施不同的代码集成与发布流程,这会影响整个研发团队每日的协作方式,因此研发团队通常需要很认真地选择自己的策略。

现状

采用的分支策略

目前我们采用的 Git Flow 模型,其在 2011 年左右被大家当作了推荐的分支模型。

在这里插入图片描述

主要包括:

  • 主分支:master,稳定版本代码分支,对外可以随时编译发布的分支,不允许直接 Push 代码,只能请求合并(pull request),且只接受 hotfix、release 分支的代码合并。gitlab 上做权限限制(开发组长)。
  • 热修复分支:hotfix,针对现场紧急问题、bug 修复的代码分支,修复完后合并到主分支、开发分支。
  • 发版分支:release,版本发布分支,用于迭代版本发布。迭代完成后,合并 dev 代码到 release,在 release分支上编译发布版本,以及修改 bug(定时同步 bug 修改到 dev 分支)。测试完成后此版本可以作为发版使用,然后把稳定的代码 push 到 master 分支,并打上版本标签。
  • 开发分支:dev,开发版本分支,针对迭代任务开发的分支,日常开发原则上都在此分支上面,迭代完成后合并到 release 分支。
  • 特性开发分支:feature-***,开发人员可以针对模块自己创建本地分支,开发完成后合并到 dev 开发分支,然后删除本地分支,涉及多人协同开发的可以 push 到服务端。

目前团队特点

  • 尚不具备主干开发能力(开发团队系统设计和开发能力非常强)
  • 有预定的发布周期
  • 需要严格执行发布周期(双周迭代)

目前落地方案

在代码分支管理的层面上,团队源代码分为五个主要分支:

  • Master:主分支,稳定版本
  • Hotfixes:补丁分支,稳定/预览版本或现场问题的应急处理
  • Release:预览分支,Bata版/测试与bug修复
  • Develop:开发分支,常规功能的新增与调整
  • Feature:特性分支,同时可以有多个特性分支,代码合并后结束;

分支合并时间:

  • 主分支:每个季度一个正式版本,于每个季度末合并发版;由预览分支、补丁分支合并;不允许直接 Push 代码,只能合并;
  • 补丁(热修复)分支:随现场使用情况而定,可以打临时版本或补丁;由主分支替换而来,修复完后合并到主分支、开发分支;
  • 预览分支:版本发布分支,用于迭代版本发布。每日测试打版验证,由开发分支合并而来;测试完成后此版本可以作为发版使用,然后把稳定的代码 push 到 master 分支,并打上版本标签。
  • 开发分支:不对外发布,可以由其他分支合并而来;针对迭代任务开发的分支,日常开发原则上都在此分支上面,迭代完成后合并到 release 分支;
  • 特性分支:不直接打版,可以由开发分支合并而来;新功能稳定后合并到开发分支;

在这里插入图片描述

目前存在的问题

  • 分支关系复杂:GitFlow 包含的分支过多,以及许多繁琐的合并规则。重流程,使用起来并不是很容易,发布分支拉出后,直到合回主干,若有特性修改或 Hotfix 需要维护多处 CherryPick(选择部分变更集合并到其他分支) 合并;
  • 集成时间滞后:特性分支在功能完成前,“不敢”随意合并回 Dev 分支,造成代码集成时间严重滞后;
  • 代码集中冲突:每次功能完成后进行“大集成”,十分容易出现大范围代码冲突;
  • 特性易合难分: 特性一旦集成到 Dev 分支便难以再次分离, 单个特性问题可能导致整体发布延期。
  • 无法自动化:各特性分支进入到稳定阶段,必然需要 merge 在一起,然后一起被编译、打包和测试。怎么让这个过程更加自动化呢?

方案选型

常见分支策略

主干开发,分支发布

在这里插入图片描述

图片来源:https://paulhammant.com/2013/12/04/what_is_your_branching_model/

在这种分支策略下,开发团队共享一条主干分支,所有的代码都直接提交到主干分支上,主干分支就相当于是一个代码的全量合集。在软件版本发布之前,会基于主干拉出一条以发布为目的的短分支。

分支开发,主干发布

在这里插入图片描述

图片来源:https://paulhammant.com/2013/12/04/what_is_your_branching_model/

当开发接到一个任务后,会基于主干拉出一条特性开发分支,在特性分支上完成功能开发验证之后,通过 Merge request 或者 Pull request 的方式发起合并请求,在评审通过后合入主干,并在主干完成功能的回归测试。开源社区流行的 GitHub 模式其实就是属于这种。
根据特性和团队的实际情况,还可以进一步细分为两种情况:

  • 每条特性分支以特性编号或需求编号命名,在这条分支上,只完成一个功能的开发;
  • 以开发模块为单位,拉出一条长线的特性分支,并在这条分支上进行开发协作。

两者的区别就在于特性分支存活的周期,拉出时间越长,跟主干分支的差异就越大,分支合并回去的冲突也就越大。所以,对于长线模式来说,要么是模块拆分得比较清晰,不会有其他人动这块功能,要么就是保持同主干的频繁同步。随着需求拆分粒度的变小,短分支的方式其实更合适。

主干开发,主干发布

在这里插入图片描述

图片来源:https://paulhammant.com/2013/12/04/what_is_your_branching_model/

团队只有一条分支,开发人员的代码改动都直接集成到这条主干分支上,同时,软件的发布也基于这条主干分支进行。对于持续交付而言,最理想的情况就是,每一次提交都能经历一系列的自动化环境并部署到生产环境上面,而这种模式距离这个目标就更近了一点。可想而知,如果想要做到主干分支在任何时间都处于可发布状态,那么,这就对每一次提交的代码质量要求非常高。在一些追求工程卓越的公司里,你要提交一行代码,就必须经历“九九八十一难”,因为有一系列的自动化验收手段,还有极为严格的代码评审机制来保证你的提交不会把主干分支搞挂掉。

优缺点对比

在这里插入图片描述

选出最适合的策略

很难说有一种通用的分支策略可以满足我们所有场景的需求。但是,有些分支策略的原则更加适合于快速迭代发布的场景,也就更加适合 DevOps 的发展趋势。所以,这里我个人比较推荐的是分支开发,主干发布的模式,也就是团队共享一条开发主干,特性开发基于主干拉出特性分支,快速开发验收后合并发布,同时,在特性分支和发布分支分别建立不同的质量门禁和自动化验收能力。

这样做的好处在于:

  • 保留“特性分支”的工作方式,便于团队协作;
  • 简化 Gitflow 的复杂分支策略,上手容易;
  • 灵活的特性分支组合集成,集成后亦可快速剥离;
  • 实现“准持续集成”
    • 略低于单主干,远高于 Gitflow 的集成频率 ;
    • 选择性的特性持续集成(方便灵活,但其实并非优点)

不过,在执行的过程中,需要遵守以下原则:

  • 团队共享一条主干分支;
  • 强力的特性拆分的能力;
  • 特性的粒度和分支存活的周期是关键要素。根据经验来看,分支存活的周期一般不要超过2周;
  • 特性分支的命名需规范;
  • 保证一个特性的关联改动需要提交到一条分支上,而不是到处都是,尽量做到原子性提交。

分支发布的策略图如下所示:
在这里插入图片描述

  • 代码管理后台:GitLab
  • 主分支:master,开发主分支,对外可以随时编译发布的分支,不允许直接Push代码,只能请求合并(pull request)。gitlab上做保护性限制。
  • 热修复分支:hotfix/版本号命名,针对现场紧急问题、bug修复的代码分支,修复完后删除。
  • 发布分支:release/版本号命名,版本发布分支,用于迭代版本发布。迭代完成后,合并代码到master,在release分支上编译发布版本,以及修改bug。测试完成后此版本可以作为发版使用,然后把稳定的代码合并到 master 分支,并打上版本标签。支持针对不同项目的特性发布。
  • 特性分支:feature/特性命名,开发版本分支,针对迭代任务开发的分支,日常开发原则上都在此分支上面。
  • 本地分支:local/特性命名,开发人员可以针对模块自己创建本地分支,开发完成后合并到 feature 特性分支,然后删除本地分支。

四、“两个披萨”团队的代码管理实际案例

在亚马逊内部有所谓的“两个披萨”团队,指的是团队的人数不能多到两个披萨饼还不够吃的地步。也就是说,团队要小到让每个成员都能做出显著贡献,并且相互依赖,有共同目标,以及统一的成功标准,这样团队的工作效率才会高。
现在有很多互联网公司喜欢采用“两个匹萨”团队的模式,你可能很好奇,这些团队通常是如何实施代码管理的?
当前国内互联网公司通常采用特性分支开发的模式,我在上面的文章中,为你详细介绍了这种模式,下面我就以这种模式为例,为你解开困惑。以迭代周期为一周的项目为例,我将按照从周一到周五的时间顺序,通过整个团队在每天的工作内容,跟你分享项目任务分配,分支创建、集成与分支合并、上线,包括分支删除的关系。你可以从中了解研发团队日常代码管理的真实情况,体会团队为了提高研发效率,在代码管理上做出的创新与改进。

背景

周一上午 11:30,“复仇者” 团队的周会结束,会议室里陆续走出了 6 名工程师:

  • “钢铁侠”:现任“复仇者”项目经理及产品负责人;
  • “美国队长”:负责“复仇者”项目的技术架构,兼开发工作;
  • “绿巨人”:全栈开发;
  • “雷神”:全栈开发;
  • “蜘蛛侠”:负责几个成熟模块的维护;
  • “黑寡妇”:资深测试工程师,负责系统集成与测试。

其他同事泡咖啡喝茶的时候,“钢铁侠”在公司的 YouTrack 上已经把 issue 分配给了团队成员,预示着忙碌又充实的一周要开始了。

周一下午

“美国队长”“绿巨人”“雷神”“蜘蛛侠”这 4 名开发人员早已熟悉团队的工作流程,午休之后,他们纷纷打开 YouTrack 界面,在待办事项上找到自己的 issue,查看无误后,直接根据 issue 建好了新的特性分支。

每个新分支代表了一个具体的任务,待四人建好新分支后,“钢铁侠”不由得微微一笑,心想:哈哈,任务都被大伙儿认领了,看样子,他们下午就要开工啦。这 4 名开发人员新建的 4 个分支,如图 1 所示。
在这里插入图片描述

这时,资深测试工程师“黑寡妇”也没闲着,开始查看起本周计划完成的 issue,整理出功能点、性能要求和粗粒度的接口列表,基本明确了测试范围。随后,她在公司 Jenkins 平台上为本周迭代设置好了“Auto Merge”(jenkins-gitlab-merge-request-builder-plugin 是高效地解决分支合并的一系列问题的插件),如图 2 所示。
在这里插入图片描述

有了 Auto Merge,任何一个分支的 PR 会自动触发合并,一旦出现冲突,开发人员就会立刻收到钉钉通知。周一下班前,4 位开发人员分别把各自的代码 PR 到了 release 分支。大家开开心心回家了。

周二

“美国队长”起了个大早,9 点半就到公司了,昨天他已经实现了核心功能,今天要完善这些功能并升级 API。他忙了个把小时,本地开发自测完成,并把本地 feature/captain 分支 push 到了 GitLab 服务器。
一分钟不到,“美国队长”的邮箱收到了 Jenkins 发来的通知,告诉他刚提交的某两个文件和 feature/hulk 分支发生了冲突。
“美国队长”知道肯定是黑寡妇创建的 Auto Merge 帮助自己快速发现了冲突,他直接用 GitLab 的 compare 功能对比了 feature/captain 和 feature/hulk 这两个 PR,找到了冲突所在的行。
通过分析,“美国队长”判断出 feature/hulk 的变更是合适的,这个冲突应该由他解决掉。
“美国队长”选择在本地对自己的分支执行 git rebase -i ,把引入冲突的 commit 进行了变更,自测通过后,再次把 feature/captain 分支 PR 到了 release 。为了确保冲突的问题已经被解决,他打开了 Gitlab ,发现状态是“已合并”(Merged) ,这才端起杯子泡咖啡去了。
团队已经约好了协作节奏:每周四下班前完成一个迭代的上线。通常周二下午开发人员要把每个 issue 的基本功能开发好,“黑寡妇”周二下午会给 release 配置好持续交付的环境,一旦某个分支 PR 后,自动完成分支合并,然后自动编译、打包,并部署到测试环境。在测试环境上,除了跑自动化测试外,“黑寡妇”也会手工做一些集成测试和性能测试。
周二下午,“美国队长”开始 review 大家的代码, Auto Merge 把本周开发的 4 个分支,在 GitLab 上分别创建了 merge request,目标分支都是 release。“美国队长”觉得 GitLab 的 review 功能很完善,交互也很便捷。这时,其他 3 名开发人员,忙着写代码和自测。“黑寡妇”除了搭建测试环境外,还补充了自动化测试的用例。

周三

经过周一和周二的努力,本周的基本功能均已实现,“黑寡妇”开始对系统实施集成测试,并做一些压力测试。上午测试时,“黑寡妇”发现在某些场景下系统存在较大的延迟,这个问题在上周的版本中并不存在。她判断是本周新引入的功能导致了这个问题,但一下子又很难确定是怎么引起的。
于是,“黑寡妇”决定回滚 Merge 的代码,把嫌疑最大的分支剔除掉后再打包测试。通过这样的方式,最后查出是 feature/thor 这个分支引入的问题,她把测试情况详尽地告诉了“雷神”。
大半个下午雷神都在查问题,到下午四点钟时,问题终于被“雷神”修复了,他把 feature/thor 分支做了 push,然后向“黑寡妇”求助,请她合入自己的分支后再帮忙做测试。
“黑寡妇”把“雷神”的分支重新 Merge 入 release,并把编译包重新部署到了测试环境。经过测试验证:延时大的问题真的不见了。
下班前,“黑寡妇”召集项目组开了个简短的质量会议,大家商量后认为本周计划内的四个开发任务集成后没有大的质量问题,周四可以一起上线。
会后,“黑寡妇”看了看本周的合并请求,“美国队长”对请求意见都是赞成合入 release ,Sonar 检查也都合格,加上自己测下来质量也过关,于是,她果断地接受了合并申请。在回家前,release 对应的最新 commit 已经顺利地编译、打包后被发到用户验收测试环境,“黑寡妇”对这个环境启动了自动化测试服务。至此,测试加修复 Bug,忙碌了一整天,大家终于可以回家休息了。

周四

“黑寡妇”一早上班时,首先查看了自动化测试的结果,显示 release 分支构建出的包符合质量要求。于是,她又对没有设计自动化测试用例的部分,进行了手工测试,发现几个界面上存在文字描述的问题,随后通知开发做修复。
开发在本地分支上修复问题后 push 到 GitLab,再次发起合并请求,“黑寡妇”逐个接受了这几个 Fix 的请求。
到中午时分,用于上线的产品包终于生成了。
等到发布窗口开启时,“黑寡妇”通过公司的发布系统把合格的产品包发布到了线上。观察一段时间,线上运行都正常。
对应本次上线,“黑寡妇”把 release 代码合并入 master 分支及时给 master 打了 tag,然后把本周成功发布的消息通知到项目组,并向“钢铁侠”做了汇报。
“钢铁侠”看大伙儿忙碌了这么多天,豪爽地请大家喝果汁,并告诉大家他又有几个紧急的用户需求,嘱咐大伙下周继续努力。

周五

通常在这一天,项目组会一起清理过期的分支,删除本周已合并到 master 的分支。而对于下周开发的新分支,项目组约定统一从 master 上拉取。另外,利用这一天,项目组也会召开回顾和改进会议,以讨论解决目前的一些已有问题的方案,这些讨论即包含工作流程问题,也包含代码和系统等问题。

小结

介绍了由 6 人组成的“两个披萨”团队代码管理的实践,通过周一到周五的具体活动,你可以看到采用特性分支开发的团队是如何创建分支、集成分支和删除分支的,希望能对我们的日常工作改进也有所帮助。

常见问题说明

单个特性分支怎么合入到发布分支?

为了保证集成分支的质量,在 gitlab 上集成分支通常都被保护起来(protected),不允许直接 push 到被保护的分支。不过,我们可以通过发起 Merge Request 的方式把特性分支合入到发布分支 。借助 Merge Request,我们可以完成 sonar 静态检查、代码 review 等质量管理的活动。

多个特性分支会给集成带来哪些问题?

  • 不同分支可能会修改相同文件,集成时很可能出现代码冲突。
  • A、B两个分支先后合入到集成分支,B合入后导致A分支对应的功能发生故障。
  • A 合入到集成分支后可能需要一套测试环境;B 合入到集成分支后也可能再需要一套测试环境。多特性分支分别合入集成分支所需的测试环境也多。

Auto Merge 功能及特点

  • 合并冲突自动告警
  • 自由选择需集成的特性分支
  • 支持 CI
  • 基于 Merge Request 扩展

如果自动集成时代码发生冲突,则 gitlab 上会提示冲突,也会通知给相关人员。如下图所示:

在这里插入图片描述

参考资料:

  • [1]:《持续交付36讲》
  • [2]:《DevOps实战笔记》
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。