0%

Trunk Based Development 主干开发模型

Preface

在之前的博文中我们介绍了 Git Flow 分支模型,正如文中所说,Git Flow 偏向于控制管理,使用了较多的分支,流程颇为复杂。大量的团队在实践过程中也遇到了颇多问题,其中大部分来自长期存在的分支。随着软件开发模型的演进,GitHub Flow、Trunk Based Development 等模型也应运而生,也已被 Google、Facebook、TW 等企业实践。本文主要介绍 TBD 模型。

Git Flow的问题

  • 合并冲突,合并冲突在使用 Git Flow 是非常常见的。原因很简单:如果你有多个并行功能分支,他们长时间存在,那么很可能代码库的相同部分在两个功能分支中被分别更改。合并冲突不仅对于需要手动解决的开发人员来说是令人沮丧的,也增加了在代码中破坏某些功能的风险,因为当你不得不决定使用哪个版本代码时,很容易犯错。

  • 功能分离,在合并到同一个分支之前,你不能测试两个功能的组合。当你在单独的分支中开发几天甚至几周的功能时,当合并回主分支后,可能也会发生两个功能的相互作用影响了你的代码。

  • 并没有做到持续交付,在 Git Flow 分支模型下,发布是非常有计划的,一个 feature 必须要经过一系列步骤才能到达生产环境,在时间上平均一个 feature 都要等待 两周时间才能长线,这样的等待并非是需求上的“按计划发布”,而是从技术上就造成了发布瓶颈,显然难以达到持续交付的要求。

  • 与持续集成相悖,你会发现,在坚持持续集成实践的情况下,feature 分支是一件非常矛盾的事情。持续集成鼓励更加频繁的代码集成和交互,让冲突越早解决越好。feature 分支的代码隔离策略却在尽可能推迟代码的集成。

GitHub Flow

GitHub Flow 是一个更轻量级的软件开发模型,示意图如下。它摒弃了 Git Flow 中繁杂的分支,只保留一个主分支 master。开发新功能时从 master 分支上拉取 feature 分支,开发完成后发起 Pull-Request,小组内进行评审和反馈,此时也进行 Code Review。测试通过后合并回主分支。

GitHub-Flow2-1

相比于 Git Flow,这种方式因为省去了一些分支而降低了复杂度,同时也更符合持续集成的思想,以一张故事卡为集成的最小单位,相对来说集成的周期短,反馈的速度也快,能够及早的遇到问题并及早解决。

顺着持续集成的思想,如果我们把 GitHub Flow 分支模型做得再极致一点,我们不要 feature 分支,或者把 feature 分支只留在本地;不需要使用 Pull-Request 而是直接 Push 到远程 master 分支,我们就做到了 Trunk based Development。

TBD

Trunk based Development,又叫主干开发,是一套代码分支管理策略,开发人员之间通过约定向被指定为主干的分支提交代码,以此抵抗因为长期存在的多分支导致的开发压力。此举可避免分支合并的困扰,保证随时拥有可发布的版本。“主干”这个词隐喻了树木生长的场景,树木最粗最长的部位是主干,分支从主干分离出来但是长度有限。

what_is_trunk

使用主干开发后,我们的代码库原则上就只能有一个 Trunk 分支即 master 分支了,所有新功能的提交也都提交到 master 分支上,保证每次提交后 master 分支都是可随时发布的状态。没有了分支的代码隔离,测试和解决冲突都变得简单,持续集成也变得稳定了许多,但也有如下几个问题:

  • 如何避免发布引入未完成 Feature,答案是使用 Feature Toggle。在代码库里加一个特性开关来随时打开和关闭新特性是最容易想到的也是最容易被质疑的解决方案。Feature Toggle 是有成本的,不管是在加 Toggle 时的代码设计,还是在移除 Toggle 时的人力成本和风险,都是需要和它带来的价值进行衡量的。

  • 如何进行线上 Bug Fix,答案是在发布时打上 Release Tag,一旦发现这个版本有问题,如果此时 master 分支还没有其他提交,那可以直接在 master 分支上 Hot Fix 然后合并至 release 分支;如果 master 分支已经有了提交就需要做以下三件事:

    • 从 Release Tag 创建发布分支。
    • 在 master 上做 Fix Bug 提交。
    • 将 Fix Bug 提交 Cherry Pick 到 release 分支。
    • 为 release 分支打上新的 Tag 并做一次发布。

说明

  • 主干开发是助力实现持续集成持续交付的关键因素。开发团队的成员一天多次地将代码提交到主干分支,满足了持续交付的必要条件。团队的工作在 24 小时内就可以被整合,这保证了代码版本随时处于可发布状态,使得持续交付成为可能。

  • 你可以选择直接向主干分支提交代码的方式(适用于小团队)或者采用 Pull-Request 的方式,只要保证特性分支不能长期存在,并且产品是独立存在的。

  • 根据团队规模和提交频率,特性分支可用于合并到主干分支前的代码审查和持续集成。这些特性分支可以让开发人员在代码合并到主干分支之前进行持续审查,而对于较小规模的团队,则可以直接向主干分支提交。

  • 根据预期的发布频率,你的团队或许需要实时从主干分支创建发布分支以确保发布版本不会有新的提交,这些分支应该在发布完成后一段时间内删除。另一方面,你的团队也可以选择从主干分支发布而不需要发布分支,并采用“修复前进(fix forward)”的策略进行 bug fix,这种发布策略适用于高吞吐量的团队(high-throughput teams)。

References