对于单一贡献者来说,由于pull请求与功能分支的存在周期较短,所以处理起来难度不大。但面对大规模团队,这种方式就往往显得不那么高效了。
对于大多数规模化团队,直接使用master往往效率更高,这是因为他们必须以安全方式进行主干开发,从而提高协作水平、增强开发质量以及实现集体所有权等等。
基于主干开发的优势
“不,我们绝对不要直接对master进行push,你疯了吗?” 好吧,我来讲个真实的故事。
我最近加入了一家咨询企业。担任顾问职务的乐趣在于,我们可以与各类客户一同工作。事实上,在最近几个月的实际工作当中,我已经与3个不同的团队进行过合作。团队成员往往身处同一地点,规模约为6到8人,主要由工程师以及产品负责人组成,有时还设有Scrum Master职位。团队成员几乎围坐在一起,当然偶尔也会有人选择在家里远程办公。
在刚刚加入团队时,我总会提出很多问题,主要是为了了解项目的基本情况以及如何为成员们提供帮助。在这3个团队当中,我们总会谈到版本控制,而且对话基本都遵循以下套路:
我:“好吧,我输入代码之后,会发生什么情况?”
他们:“你会创建一条pull请求,把对应的链接放进Slack里,然后会有人进行审查。”
我:“哦,所以不能直接对master进行push喽?”
他们:睁大眼睛看着我,就像盯着外星人……然后就是几秒钟的沉默
他们:不以为然地摇摇头“当然不行,我们不会直接push到master。代码需要首先接受审查。”
这时候,我能感受到他们正在想:“这家伙是认真的吗?这人真的是来帮忙的吗?”
当然,我不介意受到质疑,也不介意人们拒绝我的建议,毕竟这也是我日常工作的组成部分。但最令人感到惊讶的是,几乎没有人会把直接push到master看作一个可行的选项。我在谷歌上搜索过很多次,也问过其他公司的工作人员,看起来功能分支确实已经成为一种常态,并在软件开发行业中成为一大既定标准。
但我原先所在的公司主张直接push到master,所以我感觉非常矛盾,并决定写下这篇博文。我希望解释一下,为什么我坚信使用临时功能分支的团队应该试试直接对接master,以及这种作法能够带来怎样的收益。
首先,我要解释一下我观察到的使用临时功能分支的团队的典型工作流程:
具体工作流程可能有所不同,但相信我的描述对大多数朋友来讲都不陌生。这样的工作流程,也就是现在大家耳熟能详的GitHub Flow。
首先我要强调一点,我知道有些团队会采用其他的分支使用方式,例如具有长期分支的Gitflow。这些分支策略也有自己的问题。但在本篇文章中,我希望着重说明临时功能分支带来的问题,以及我为什么认为基于主干的开发方法能够很好地解决这些问题。
我提出的替代方法,就是直接在master上工作,不再使用分支。当然,必须要以安全的方式进行。为了实现这个目标,团队需要适当采取以下关键实践:
首先需要在团队内部普及结对编程。所谓结对编程,就是在开发人员之间结成合作伙伴,大家实时查看对方正在编写的代码,因此无需后续创建pull请求。事实上,结对编程能够为团队带来一系列重要的收益,包括:提高团队的适应能力、人们不必为其他杂事分心、工作完成速度更快、更高的开发质量、团队的整体编程风格更加标准、人们能够共同找到解决问题的好方法、以及知识共享等等。
在某些情况下,我们甚至可以将结对编程进一步升级为“全民编程”,即让整个团队在同一时间通过同一台计算机从事同一项工作。(例如让整个团队同时进行架构或设计决策。)
大家还需要选定一套值得信赖的build,其需要能够运行充分的造化测试以保证代码库处于可发布状态;其速度必须够快,以保证我们可以在代码被push到master之前完成本地构建,从而避免push本身对团队其他工作内容造成破坏。
为了实现这两点,团队通常需要遵循TDD、BDD之类的实践,同时建立起行之有效的测试策略——例如利用测试金字塔以最大程度减少慢速测试状况的出现。
如果该build被破坏,那么修复工作应马上成为整个团队的首要任务。如果无法快速完成修复,则应立即还原以撤销造成破坏的变更。
在任何一个时间点上,master当中都会包含某些尚未完成的代码。其不会造成任何危害,因为这部分代码并未被实际应用在生产路径当中。但是,将这些代码包含进master也有很多好处:我们可以更快注意到集成问题,可以进行更复杂的重构,也可以通过将代码对接到测试以证明代码的工作效果。
在实践当中,我发现大多数情况下,大家都可以使用“抽象分支”模式避免在生产环境中使用尚未准备就绪的代码。对于更复杂的情况,我个人会使用功能标记机制。
在之前的公司,这是一种规范化的工作方式。我在那里工作了近6年,而且他们早在十多年前就已经在采用这种方法。因此,我可以确定这是一种可行且能够成功推广的开发思路(文末将对这种工作方式做出详尽说明)。
我认为,以上提到的方法能够为开发团队带来诸多收益与优势,也应被视为团队需要追求的重要目标。在实际贯彻之后,我们可以直接将新代码push到master,或者说跳过分支环节。以下是我亲身体会到的具体优势,顺序不分先后(当然,根据您的实际情况,大家可能会更重视其中的某些优势):
这里让我具体说明一下:基于主干开发本身并没有什么特别之处,也无法给我们带来特殊的收益。真正让我们受益的,其实是实现基于主干开发所必须具备的各种实践能力。对于任何一位能够直接在master上工作的团队而言,他们必须知晓如何在不破坏原有代码的情况下工作,如何编写良好的测试,以及如何高效合作等等。
可以说,基于主干的开发正是团队健康的一大标志。确实,《加速( Accelerate)》一书中提到:在对1万多名员工与2000个组织进行研究之后,他们发现基于主干开发的团队与业务水平优异的团队之间存在着强相关性。他们还发现,在高效能团队当中,分支的存在周期通常不到一天。
我曾经询问不少团队,他们为什么要使用功能分支,或者是为什么不应该直接将代码提交至master,而得到的答案通常是“我们必须确保master处于良好状态,master应该随时可以发布”或者“我们需要审查代码以确保其质量符合我们的标准”等等。我对这两种观点都很认同,但在本文中,我希望向大家介绍如何在无需分支的前提下获得相同的结果。
但实际上,很多团队之所以倾向于使用功能分支,背后还有一个不为人知的理由:如果每个人都在自己的分支中工作,那么开发流程将变得更加轻松高效,因为他们不会彼此掣肘。
这样的说法没什么错误,但我仍然表示强烈反对。事实上,很多团队问题正是因此而引发。大多数团队主要针对个人效能——而非团队效能——进行优化。他们着眼于每个人的生产力,而非团队的总体生产力。用精益术语来讲,这就是一种典型的优化资源效率、而非优化流程效率的例子。
功能分支优化的是个人效能,而基于主干开发则强调优化团队效能。当针对团队效能做出优化时,个人的效率看起来反而会变慢。这是一种重要的范式转变,而且我承认有一点反直觉。
说了这么多,这里我还要再澄清一下:我绝对不是在否定功能分支的意义。在某些情况下,功能分支模式非常有用。根据经验,我的观点是当代码拥有明确的所有者、但其他人也需要同步协作时,功能分支是个不错的选择。
最典型的例子就是开源代码模型:在一个典型的开源项目当中,通常存在一个明确的所有者——可能是一个人,也可以是一个核心团队。与此同时,来自世界各地的贡献者会以不同的时区进行工作与交流。在这种情况下,要求贡献者发送PR绝对非常重要,因为所有者必须要对内容进行审查。实际上,这也正是GitHub发明PR的原因所在!如此一来,维护者能够更轻松地拒绝他们无法接受的PR(例如某些未经讨论通过的PR)。
在使用内部开源模式的企业中,有时也会出现类似的情况:某个团队拥有代码,但由于太过忙碌而无暇处理这些代码;另一个团队则通过发送PR为这部分代码做出贡献。这虽然不是解决问题的唯一方法,但有时候确实是个不错的折衷性方案。
如果大家目前正在使用功能分支,而且打算过渡到直接在master上工作的状态,那么请参考以下建议:
扩展资源
除了文章内给出的链接外,以下资源也能帮助大家更深入地理解这一主题:
这里要特别感谢Will、Aram以及Pritesh的早期反馈,也感谢他们多年来帮助我建立并完善这样一套理论。另外,也感谢Jürgen Gmach审阅本文草稿并提出改进建议。
附录:基于主干开发的实践方法
下面,我将向大家介绍我自己之前的团队如何实施基于主干开发以及面向master直接提交。相关背景与文章开关的表述基本一致:团队成员约为6到10人,大家身在同一地点;其中开发人员4到6名,测试人员1到2名,1位业务分析师,外加1位团队负责人。
原文链接: Why I love Trunk Based Development (or pushing straight to master)
领取专属 10元无门槛券
私享最新 技术干货