做软件开发的,每天都在跟MR(合并请求)打交道。
提交代码、创建MR、等人评审、修改代码、合并上线,这是每个开发者的日常。看起来很简单的流程,但真正做起来,坑真的不少。
我们团队这几年一直在优化MR工作流,从最早的简单提交合并,到现在有完整的分支策略、评审规则、CI/CD集成。在这个过程中踩了很多坑,有些坑让我熬了好几个通宵才解决。
这篇文章我想分享一下在MR工作流中遇到的各种坑和解决方案。从分支管理、代码评审到CI/CD集成,聊聊那些让我熬夜排查的问题和最终的解决办法。
如果你也在做MR工作流的建设,或者经常被MR相关的问题困扰,希望这篇文章能帮你少踩一些坑。
坑一:分支管理混乱
第一个坑是分支管理混乱。
我们团队最早的时候,分支管理很随意。每个人从主分支拉一个分支,想怎么命名就怎么命名,想什么时候合并就什么时候合并。分支多了之后,根本分不清哪个分支是做什么的,哪些已经合并了,哪些还在开发中。
有一次,一个同事不小心把一个还没开发完的分支合并到了主分支,导致线上出了bug。我们花了好几个小时才找到原因,回滚了代码,才恢复正常。
还有一次,两个同事同时改了同一个文件的同一部分代码,合并的时候冲突特别严重,花了整整一天才解决完冲突。
这些问题让我们意识到,必须有一套规范的分支管理策略。
后来我们引入了Git Flow的简化版,规定了几种固定的分支类型:主分支(main/master)永远保持可发布的状态,只有通过评审和测试的代码才能合并进来。开发分支(develop)是日常开发的集成分支,新功能都从这里拉分支,开发完合并回来。功能分支(feature/xxx)用于开发新功能,从develop拉出来,开发完合并回develop。修复分支(hotfix/xxx)用于修复线上bug,从main拉出来,修复完同时合并回main和develop。
同时规定了分支命名规范,比如feature/xxx-功能描述、hotfix/xxx-bug描述,xxx是对应的需求或者bug编号。这样一看分支名就知道这个分支是做什么的。
分支管理规范之后,混乱的情况大大减少了。每个人都知道该从哪里拉分支,该合并到哪里,分支命名也统一了,管理起来清晰很多。
坑二:代码评审流于形式
第二个坑是代码评审流于形式。
刚开始推行代码评审的时候,大家都不太习惯。评审人随便看两眼就点了通过,根本没有认真看代码。结果就是,评审做了,但bug和坏味道还是照样流到主分支。
有一次,一个同事提交的代码里有一个很明显的SQL注入漏洞,评审的时候居然没人发现。上线之后被安全扫描扫出来了,差点造成安全事故。
还有一次,评审的时候大家都没仔细看,合并之后才发现代码里有个死循环,导致线上服务CPU飙升,差点宕机。
这些问题让我们意识到,代码评审不能只是走个过场,必须真正发挥作用。
后来我们做了几个改进:
第一,明确评审的检查项。我们整理了一份代码评审检查清单,包括功能是否正确、有没有明显的bug、代码风格是否符合规范、有没有安全隐患、性能有没有问题、测试是否充分等。评审人按照清单逐项检查,而不是凭感觉看。
第二,限制每次MR的代码量。如果一个MR改了几千行代码,评审人根本看不过来。我们规定每个MR的代码改动尽量控制在400行以内,超过的话建议拆分成多个MR。这样评审人能认真看完每一行代码。
第三,评审人要真正理解代码。我们要求评审人不能只看表面,要理解代码的逻辑,思考有没有边界情况没考虑到,有没有更好的实现方式。发现问题要明确指出来,而不是含糊地说"这里可能有问题"。
第四,建立评审文化。我们强调代码评审不是挑刺,而是互相学习、共同提高。评审人要尊重提交者,提交者也要虚心接受建议。大家的目标是一致的,就是把代码质量提上去。
做了这些改进之后,代码评审的质量明显提高了。很多bug和坏味道在评审阶段就被发现了,线上的问题少了很多。
坑三:CI/CD集成问题
第三个坑是CI/CD集成的问题。
我们在MR流程中集成了CI/CD,每次提交代码都会自动跑构建、测试、代码检查等。但集成的过程中遇到了很多问题。
第一个问题是CI跑得太慢。
刚开始的时候,CI要跑十几分钟甚至半个小时。开发人员提交代码之后,要等很久才能看到结果,严重影响开发效率。很多人等不及,就直接跳过CI合并了,导致CI形同虚设。
我们做了几个优化来加快CI速度:
一是并行化。把测试、代码检查、构建等任务并行执行,而不是串行。这样总时间从最长的那个任务决定,而不是所有任务的总和。
二是缓存。把依赖包、构建产物等缓存起来,下次运行的时候直接用缓存,不需要重新下载和构建。
三是增量测试。只跑受改动影响的测试,而不是全量跑。当然这个需要有完善的测试依赖分析。
优化之后,CI的时间从十几分钟降到了三五分钟,大家就愿意等了。
第二个问题是CI不稳定。
有时候CI会无缘无故失败,重新跑一次又好了。这种不稳定的CI最让人头疼,因为你不知道是代码真的有问题,还是CI本身的问题。
常见的原因有:测试用例不稳定(flaky test)、依赖服务不稳定、资源不足、并发冲突等。
我们的解决方法是:
一是治理flaky test。把那些不稳定的测试用例找出来,修复或者标记为暂时跳过。定期检查flaky test的情况,确保数量在可控范围内。
二是确保依赖服务稳定。CI依赖的数据库、缓存、第三方服务等,要确保稳定可用。可以用容器化的方式,每次CI启动干净的依赖服务。
三是给CI足够的资源。CI跑得慢或者不稳定,很多时候是因为资源不够。给CI服务器配足够的CPU和内存,必要时扩容。
第三个问题是CD(持续部署)的风险。
我们最早的时候,MR合并之后自动部署到生产环境。但有一次,一个有问题的MR合并了,自动部署到线上,导致服务挂了半个多小时。
后来我们调整了CD策略:
MR合并之后自动部署到测试环境,跑自动化测试。测试通过之后,手动触发生产环境的部署。生产环境部署的时候,先灰度发布一小部分流量,观察没有问题再全量发布。
这样虽然多了一步手动操作,但安全性大大提高了。毕竟生产环境的稳定性是最重要的。
坑四:合并冲突处理不当
第四个坑是合并冲突处理不当。
多人协作开发的时候,合并冲突是难免的。但冲突处理不好,会导致代码丢失、功能异常、甚至引入bug。
我们遇到过几次因为冲突处理不当导致的问题。
有一次,两个同事同时改了同一个配置文件,合并的时候有冲突。处理冲突的同事比较粗心,把另一个同事的配置给覆盖掉了。结果上线之后,某个功能因为配置缺失而异常,查了很久才找到原因。
还有一次,处理冲突的时候,不小心把一段重要的代码删掉了。因为那段代码是处理边界情况的,测试的时候没覆盖到,上线之后遇到边界情况就出bug了。
为了避免这些问题,我们做了几个规定:
第一,及时同步主分支。开发分支要经常同步主分支的最新代码,不要等到要合并了才同步。同步得越频繁,冲突就越小,越容易处理。我们要求至少每天同步一次。
第二,冲突要认真处理。处理冲突的时候,不能简单地选"用我的"或者"用他的",要理解两边的改动,想清楚合并之后应该是什么样的。如果不确定,就找对方一起商量。
第三,处理完冲突要测试。冲突处理完之后,要跑一遍测试,确保代码能正常编译、功能正常。不要处理完冲突就直接提交,很可能有问题。
第四,大的改动提前沟通。如果要改公共的文件或者核心模块,提前在团队里说一声,让其他人知道,避免同时改导致大冲突。
做了这些规定之后,冲突导致的问题大大减少了。
坑五:MR数量过多,评审积压
第五个坑是MR数量过多,评审积压。
团队人多了之后,每天都有很多MR提交。评审人有时候忙不过来,MR就积压在那里,等好几天都没人评审。
MR积压的影响很大。开发人员的代码合不进去,影响后续的开发;代码放久了,和主分支的差异越来越大,合并冲突越来越多;评审人看到MR放了很久,也不愿意认真看了,容易流于形式。
我们遇到过最严重的时候,有二十多个MR在等着评审,最老的已经放了一个星期了。
为了解决这个问题,我们做了几个改进:
第一,规定评审响应时间。我们规定,MR提交之后,评审人要在4小时内开始评审,24小时内完成评审。如果做不到,要提前说明,让提交者找其他人评审。
第二,轮流做评审人。不要总是让几个人评审,大家轮流来。每个人都有评审的责任,也都有被评审的权利。这样能分摊评审的压力,也能让每个人都从评审中学到东西。
第三,减少不必要的MR。有些很小的改动,比如改个错别字、调个参数,不需要走完整的MR流程,可以直接提交。我们规定了一些可以直接提交的场景,减少MR的数量。
第四,提高评审效率。评审人要专注,评审的时候不要一边聊天一边看。可以固定每天的评审时间,比如上午十点和下午三点,集中处理MR,而不是随时被打断。
做了这些改进之后,MR的平均评审时间从两天降到了半天,积压的情况基本没有了。
坑六:回滚流程不完善
第六个坑是回滚流程不完善。
不管流程多完善,总有出问题的时候。出了问题之后,能不能快速回滚,就非常关键。
我们早期的时候,回滚全靠手动。出了问题之后,开发人员手动git revert,然后重新走MR流程,重新构建部署。整个过程要十几分钟甚至更久,对业务影响很大。
有一次线上出了一个严重的bug,需要紧急回滚。但因为回滚流程不熟练,加上当时很紧张,操作出了错,导致回滚花了半个多小时,影响了很多用户。
那次之后,我们下决心完善回滚流程。
第一,一键回滚。我们在CI/CD系统里做了一键回滚功能。每个版本都有记录,出了问题之后,点一下按钮就能回滚到上一个版本。回滚的过程是自动化的,不需要手动操作,又快又安全。
第二,回滚演练。我们定期做回滚演练,确保回滚功能正常可用,每个人都知道怎么操作。这样真出问题的时候,就不会手忙脚乱了。
第三,灰度发布。前面说过,生产环境发布的时候先灰度,只放一小部分流量。这样即使出了问题,影响的用户也很少,回滚的压力也小。
第四,回滚之后的复盘。每次回滚之后,都要做复盘,分析为什么会出问题,怎么避免下次再出。不能回滚完就完事了,要从问题中学习。
完善了回滚流程之后,我们再出问题的时候,几分钟之内就能回滚完成,对业务的影响大大减小了。
坑七:权限管理混乱
第七个坑是权限管理混乱。
早期的时候,团队里每个人都有主分支的推送权限,想合并就合并,想直接推就直接推。这样很危险,万一有人不小心推了有问题的代码,直接就影响线上了。
有一次,一个新同事不熟悉流程,直接把代码推到了主分支,而且代码还没测试。结果主分支的构建失败了,影响了所有人的开发。
后来我们做了严格的权限管理:
第一,保护主分支。主分支设置为保护分支,不允许直接推送代码,只能通过MR合并。而且MR必须通过评审和CI才能合并。
第二,分级权限。不同的人有不同的权限。普通开发人员只能提交MR和评审,核心开发人员可以合并MR,管理员可以配置规则和处理特殊情况。
第三,审批规则。不同的模块有不同的审批要求。核心模块的MR需要两个以上的人评审,而且必须有该模块的负责人审批。普通模块的MR一个人评审通过就行。
第四,定期审计权限。人员变动的时候,及时调整权限。离职的人马上收回权限,转岗的人调整对应的权限。定期审计权限列表,确保没有多余的权限。
权限管理严格之后,误操作和违规操作基本没有了,主分支的稳定性大大提高。
经验总结
踩了这么多坑,我们也总结了一些经验。
第一,流程要简单高效。
MR工作流不是越复杂越好,而是越简单越好。流程太复杂,大家会觉得麻烦,就会想办法绕过。简单高效的流程,大家才愿意遵守。
我们的原则是,能自动化的就自动化,能简化的就简化。比如CI自动跑、评审提醒自动发、版本号自动生成,这些都不要人工来做。
第二,规则要明确,并且大家都认同。
所有的规则都要写清楚,让每个人都知道。而且规则要大家一起讨论制定,而不是领导拍脑袋决定。只有大家认同的规则,才会被认真遵守。
我们的MR工作流规范,是整个团队一起讨论出来的。每个人都有发言权,都可以提意见。最终的规则是大家都认可的,执行起来就很顺利。
第三,工具要支持流程。
好的流程需要好的工具来支持。我们用的代码托管平台,支持分支保护、评审规则、CI/CD集成、权限管理等功能,这些功能让我们的流程能落地执行。
如果工具不支持,再好的流程也很难执行。所以选工具的时候,要考虑它对工作流的支持程度。
第四,持续优化。
工作流不是一成不变的,要根据团队的情况和遇到的问题,持续优化。我们每季度都会回顾一下MR工作流,看看有什么问题,有什么可以改进的地方。
比如我们发现评审积压,就优化了评审机制;发现回滚太慢,就做了一键回滚。每次优化都让工作流更好用一点。
第五,培养良好的协作文化。
工具和流程是外在的,真正重要的是团队的协作文化。大家互相尊重、互相信任、互相帮助,代码评审就不是挑刺,而是共同提高;遇到问题就不是互相甩锅,而是一起解决。
我们团队一直强调协作文化,鼓励大家多交流、多分享、多帮助。有了好的文化,工作流才能真正发挥作用。
写在最后
MR工作流看起来简单,但真正做好并不容易。从分支管理、代码评审到CI/CD集成、权限管理,每个环节都有很多细节,每个细节都可能出问题。
但只要认真对待,持续优化,就能建立一套高效、稳定、适合自己团队的MR工作流。好的工作流能让团队的开发效率更高,代码质量更好,线上问题更少。
这篇文章分享的只是我们团队遇到的一部分坑,还有很多其他的问题和经验。每个团队的情况不一样,遇到的问题也不一样,但解决问题的思路是相通的。
如果你也在建设MR工作流,或者遇到了类似的问题,希望这篇文章能给你一些参考。也欢迎大家分享自己的经验,一起交流学习。
最后用一句话来结束这篇文章:"好的工作流不是束缚,而是保障。它让每个人都能专注于创造,而不是担心出错。"
愿你的团队,也能有一套好用的MR工作流。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录