- Created by 郭强, last modified on Jan 04, 2024
代码版本管理作为项目管理的一部分,应当有一定的规范约束,目的是避免混乱、提高协作效率。由于Git
已经成为主流的代码版本管理工具,虽然使用简单,但在团队使用中也应该对齐使用规范,否则在代码版本管理中反而会浪费一些毫无意义的成本开销。如果团队的版本管理中出现以下常见问题时,团队管理者应当要深思了:
- 分支管理中反复解决重复的代码冲突
CodeReview
环节繁琐,或者流于形式- 测试团队在版本流程中介入困难,难以保障质量
- 版本"超车"发布或者版本回滚困难
一、Git Flow概览
稳定分支
稳定分支通常使用主分支main
,表示当前生产正在稳定运行的代码版本。
预发分支
预发分支通常使用staging
,表示正在处于最后功能特性验证阶段的代码,验证完成后即上生产。由于修复分支的存在,因此预发分支在每一个迭代版本合并之前应当与主分支main
完成一次merge
,保证代码的同步。
迭代分支
使用敏捷开发的团队管理,迭代分支通常是一次sprint
特性功能集合,用于管理该sprint
的功能特性代码集合。每一次迭代开始时,迭代分支从稳定的主分支分流出来,通常以sprint-vx.y.z
的形式来命名。每当开发分支开发以及自测后会通过MR/PR
的形式合并到spint
迭代分支中,并通过自动化机制发布到固定的验证环境,便于测试团队进行统一的功能验证。
开发分支
开发分支是从特定的迭代分支分流的代码分支,用于完成特定迭代分支中特定功能特性的分支。开发分支通常是以迭代分支-特性名称
命名,但有的时候,开发分支会以开发者个人名义从迭代分支分流出来,以迭代分支-开发者姓名
来命名,用于管理开发者在这次迭代的多个功能特性集合。一个开发分支管理多个特性的优点是在一个人负责当前迭代的多个功能时比较方便,但缺点是如果想要将开发分支中的多个功能特性拆分上线,可能较为困难。
修复分支
修复分支为了快速修复生产环境存在的紧急问题,通常会绕开预发分支而直接合并入主分支。由于修复分支可能会绕开测试团队,难以保障质量,所以我们应当尽可能减少这样的情况。一般的问题修复建议使用迭代分支来实现,例如,sprint-v1.1.1
是对sprint-v1.1.0
版本的一次修复,修复完成后合并入预发分支,在预发分支完成验证后再合并入主分支完成上线。
标签与里程碑
稳定分支在发布生产之前需要打上标签,如v1.1.0
、v1.2.1
。标签用于标识在稳定分支的某一个Commit
是一个里程碑,用于更易人工理解的备注以及版本维护。这样在后续CICD
流程中,对版本进行升级/回滚将会更友好。标签对于使用该服务的使用方来讲,也会更易于理解和响应。
标签的命名采用国际通用的GNU
命名规则:
MajorVersion.MinorVersion.Revision
即:主版本号.子版本号.修正版本号
如:v0.0.1
, v1.1.0
, 1.7.1
MajorVersion
主版本号:版本号通常是从0
开始的,主版本号的变更表示一个全新的版本,例如:重大的架构调整、重大的不兼容性变化。MinorVersion
子版本号:发布较大的新feature
功能,或者一次完成一次迭代计划,会增加子版本号。Revision
修正版本号:往往是bug fix
,或者增加较小的feature
,较小的功能改进或者模块变化。- 当主版本号增加时,子版本号及修正版本号置
0
。 - 当子版本号增加时,修正版本号置
0
。 - 每一次版本修复时,修正版本号加
1
。
二、Git flow中的测试环节
研发自测
这个阶段测试团队资源不会介入。
每个研发同学需要优先对自己的开发内容负责,因此在每一个功能特性的开发分支合并入迭代分支之前应当完成自测。自测方式通常以本地调试、单元测试为主,如果有一些外部依赖可以在这个环节通过mock
方式来测试,保证一定程度的代码质量。
如果是复杂的功能特性,在外部资源条件允许的前提下,可以构建研发自己使用或者公共协商使用的测试环境,加上外部依赖同时部署在环境中,以完成功能特性的自测。
研发同学保证自测完成后,再提交MR/PR
将自己的开发分支合并入迭代分支。
迭代测试
这个阶段测试团队资源可以介入。
每一个功能特性通过开发分支的形式合并入迭代分支中,并通过自动化CICD
或者测试同学手动部署的方式,会将迭代分支自动部署到一个统一的测试环境中。为尽可能保证环境的稳定性,这个阶段研发同学不能随便操作更新环境中部署的服务版本。测试资源将在这个环境中对每一个功能特性进行case by case
验证。
当然,功能特性与功能特性之间可能会存在相互影响,通常有多种方式可以避免,但都有优缺点,可以选择团队可以接受的方式:
- 快速合并部署:每一项功能特性合并入迭代分支后则部署服务版本,测试同学快速进行验证。
- 优点:针对迭代速度较快的团队较友好。
- 缺点:难以避免后续功能特性之间的相互影响,有的
case
已经验证完,后续可能再出问题。
- 稳定合并部署:迭代的所有功能特性完成后再部署迭代分支,测试同学再介入验证。
- 优点:尽可能保证功能的稳定、对测试同学友好。
- 缺点:这种方式所依赖的迭代管理周期会相对较长。
- 自动化测试机制:测试同学使用自动化测试(例如自动化的接口测试)来完成大部分的功能验证,小部分的场景手工验证。
- 优点:不依赖迭代分支的部署频率、自动化测试
case
库可以在各阶段测试环节中循环使用。 - 缺点:对测试同学要求较高,需要测试同学提前构建良好的自动化测试机制。
- 优点:不依赖迭代分支的部署频率、自动化测试
Team Leader
对成员的Code Review
可以在这一阶段进行,通常TL
时间都比较紧张,因此研发同学或者TL
可以指派其他一个或多个对该功能特性相对熟悉的团队成员进行交叉Code Review
以提高分支合并效率。Code Review
也是团队内部协作中相互学习和加深"感情"的重要环节。
值得注意的是,如果一个功能特性的MR/PR
时间周期较长,可能会造成该功能特性延期,造成项目管理风险和跨团队间协作的信誉成本,因此研发同学应当在MR/PR
提交后,持续推进开发分支尽快合并入迭代分支中。
当迭代分支验证没有问题后,则可将迭代分支请求合并入预发分支。
预发回归
这个阶段测试团队资源可以介入。但有的团队通常会忽略这个阶段的建设,而是直接采用生产验证。
当迭代分支验证完毕后,即可由TL
或者迭代的Owner
合并入预发分支,预发分支可由测试同学部署到预发环境中,用于最后部署生产环境前的最后回归验证。如果团队规模较大,可能存在多个迭代并存的情况,可以根据迭代的时间安排、测试情况,让特定的某一迭代分支合并进入预发分支并进行最后的预发回归验证。
预发环境也可以看做是生产环境,但部署的是一个待发布的分支版本。预发回归的验证时间通常会很短,并且同时有且仅有一个迭代在预发分支回归测试中。
如果测试同学之前有自动测试case
库,那么可以在这里跑一遍自动化测试即可,可以极大提高验证效率。
当预发分支验证没有问题后,则可将预发分支合请求并入稳定分支。
生产验证
这个阶段测试团队资源需要介入。
稳定分支打好标签后,则将该标签对应的代码内容部署到生产环境,并由测试同学在生产环境进行验证。
由于我们本章节主要讨论代码版本管理,因此不延伸讨论灰度、蓝绿等发布和验证策略。
三、自动化工具建设
从上面的分支管理和测试环节介绍我们可以看到,整体的版本管理其实还是较繁琐的,繁琐的事情如果是依赖人工维护可能会引起不规范的操作,也可能降低维护效率。在部分环节我们可以建设对应的工具来规范化、简化版本管理,提高团队协作的幸福指数。
持续集成(CI
)
持续集成工具通常采用触发机制,例如,通过Git
版本管理服务端的WebHook
机制。
持续集成的常用能力:
- 对提交代码的静态检测、安全检测、单元测试。
- 迭代分支内容的代码自动编译。例如,二进制、镜像的编译。
- 与第三方构建、发布系统的对接。例如,推送镜像到外部镜像仓库,对接
Jenkins
、ArgoCD
等发布系统。
在以下环节,持续集成工具能够发挥重要作用:
- 开发分支→迭代分支
- 迭代分支→预发分支
- 稳定分支中标签的创建
持续部署(CD
)
持续部署通常使用持续集成来自动构建发布资源,实现自动对接,并使用第三方发布系统来实现自动发布,或者手动发布。
持续部署通常需要使用到模板和变量来维护发布资源,例如,各个服务器的账号密钥、服务组件的业务配置项等。
自动化测试
自动化测试是为了提高测试的效率,提高测试case
库的可复用性。通常随着迭代的启动,测试同学会根据自身对业务场景的理解,结合研发同学预先定义好的api
接口数据结构(UI
界面或后端接口),构造输入输出的case
库,将服务接口当做黑盒,对接口输入输出进行断言判断接口实现是否符合预期。
自动化测试在前期是一项很枯燥的工作,但随着case
库的沉淀积累,到中后期能极大保障项目质量。长期发展的大型项目,自动化测试应当尽早建立。例如oracle
数据库的自动化测试,跑一次需要耗费数天的时间,期间研发同学可以喝喝咖啡写写开源项目。但是一旦跑通过,基本可以断定产品功能不会有问题。
四、一些常见问题
rebase/squash merge
有的研发同学喜欢使用git rebase
将本地的分支代码整理整洁(保留为一个commit
)后再提交合并请求。有的研发同学不习惯使用git rebase
,而是在服务端合并时使用squash merge
。两者的效果都是一样的:在合并到目标分支后仅有一个commit
存在,方便后续追溯查看和cherry pick
。两者的区分只是使用习惯罢了。
根据笔者在部分团队踩过坑的经验来看,使用git rebase
整理本地分支可能会遇到一些困难、需要一定技巧,对于使用者来说有一定要求,有时会降低分支管理的效率。因此,如果没有特殊的rebase
习惯,或者用不好,那么个人建议直接使用服务端的squash merge
即可。
- No labels