关于DVCS、持续集成和特性分支

本文来自于持续集成英文站的文章《On DVCS, continuous integration, and feature branches》。

为了吸引大家的注意力,我想说:“特性分支是邪恶的化身”。

自2008年起,Mercurial (最近是Git)就成了我日常工作的工具,而且我喜欢使用分布式版本控制系统。正如《持续交付》一书中讨论的那样(英文版第393页和394页),有很多理由说明,与之前已存在的同类工具相比,DVCS代表了一种巨大的转变。但正如所有强大的工具一样,你会有很多种方法来使用它们,但并不是所有的方法都是好的。这里所有的讨论不是想说DVCS不好:使用特性分支和使用DVCS完全是正交的。而且,我认为,DVCS的支持者用这种工具的功能分支来推销DVCS,是对DVCS的一种伤害。

首先看几个定义。请注意,一些人以不同的方式使用这些术语,所以你需要暂时从你的大脑中把先把其它的定义擦去,否则我的讨论就没有什么意义了。

持续集成 是一种实践,用于确保你的软件一直是可以工作的,并且在几分钟内你就能够得到关于 “你的修改是否破坏了系统”的反馈。
特性分支是一种实践, 使用它的人直到他所开发的特性“完成”后才合并回主干(这里的“完成“不是“done done1”原则中的那个“完成”)。
主干是指开发主线—— 通常是指版本控制库中的。你的某交构建就是从这个主干中的一个版本创建的,并会被放到你的部署流水线中。 注意,这些定义对DVCS和其它开源项目,甚至是GitHub,也完全适用。

先让我们解决一个象稻草人一样的争论。当使用版本控制工具时,你就使用一个分支来工作:即本地工作目录。使用DVCS,只是多了一个层次而已,因为你的本地库就是一个有效分支。我并不反对创建分支。我所不赞同的是:让你最终要发布的代码在分支上堆积起来。
下面是我所看到的状况。当你将最张要发布的代码大量地堆积在分支上,会有几个糟糕的事情发生:

  • 这种状态持续时间越长,就越难合并。因为随着其他人向主干提交,主干的代码与你的分支上的代码之间的差异会越来越大。强大的合并工具的确会在一定程度上帮助你,但是任何一个做过很多开发工作的人都会有过这样的经历,即:代码的确成功合并了,但是应用程序运行不起来。随着需要合并的那些代码数量的增加,以及初始分支与最终合并时间的增长,发生这种事情的可能性是递增的,而且不仅仅是线性递增。
  • 在分支上的工作越多,在合并到主干时越有可能破坏系统。每个人都有这样的经历:有个问题出现了,你似乎找到了一个非常聪明的解决方案。可是,几个小时(或者几天)后,你才发现需要废弃所有的东西,或者(更常见地)那些引起未预期后果的代码提交。
  • 当多个开发人员同时在这个代码库上工作,并使用特性分支开发时,使代码重构变得很难。如果我重构并且提交到主干了,而其他人在其分支上已经修改了很多东西的话,当他们向主干合并时就更难了。这就会驱使我不做重构。而不充分的重构就等于劣质代码。

当大家频繁将其代码合并到主干时,就没有这些问题了。如果不这么做的话,随着团队人数的增加,这些问题的痛苦会成指数级增加。而且,还有一个恶性循环: 这种痛苦的自然反应是:更少做合并。正如我喜欢说的,当某些事情令你很痛苦时,解决方案是更频繁地做,并将痛苦提前。在这里,解决方案是每个人都更频繁地合并到主干。

然而,如果你正在做的一个功能中需要做很多工作,或者你对系统进行大面积修改时,这么做就比较困难了。下面是一些解决方案:

  1. 将Story分解成一些更小块的工作(有时被叫做Task)。我还没有遇到过一大块工作无法分解成更小块的工作——通常少于1小时,并且肯定用不了一天时间——这样让我即能达到目标,又能保证系统可工作且可发布。这需要细心的分析、讨论、思考和严格的纪律。当我无法找到在增量开发方式下几小时内可以完成的工作时,我就会先对某些想法做一些试验(敏捷中叫做Spike2)。至关重要的点在于:我能尽早地对我的方案进行快速验证:是可以解决问题,还是会对系统造成不良后果,影响了其他人的工作,或者引出了回归缺陷(这也是持续集成的动机所在)。
  2. 在实现Story时,最后再做面向用户那部分接口。先从业务逻辑及以下部分下手。使用TDD完善你的代码。频繁提交,与主干合并。最后再完成界面部分。
  3. 使用抽象分支方法 来对复杂或大范围变更进行增量开发,同时保证系统一直处于可工作状态

你怎么知道什么时候没有合并的东西太多了呢?想像一下,假如你是一个开源项目的维护者,有一个你不认识的刚刚提交了一个补丁。你会马上合并它吗?你能记住它与主干之间的所有diff吗?你能保证在不需要问你的前提下,团队其他人员能在一分钟之内就能清楚地理解这些diff吗?如果你对上述所有问题的回答不是“Yes”, 那么你就要停止工作,将其分成更小的工作单元。
很明显,只要 “特性”足够小,我并不真正反对特性分支。然而,通常使用特性分支的人大多无法通过上一段中的问题测试。真正有经验的开发人员能理解使用特性分支与为了高效使用它而建立的纪律之间的平衡,但仍旧有一定的危险——GitHub就被Forks搞得很乱,这些Forks很多是不可合并的,因为它们与主干的差异太大了。

我更想强调的是下面这个观点,即:让“尽早地持续地交付有价值的软件” 中最重要的实践之一就是确保你的系统一直是可工作的。

对于开发人员来说,达到这一目标最好的方法是:通过确保他们可以将“提交可能对系统造成破坏”的风最小化。我们可以通过“保持小步提交、在主干持续集成,并且有全面的自动化测试套件来验收本次修改的预期行为,且不会引发回归缺陷”,从而达到这一目标。

About 乔梁

乔梁,百度项目管理部高级架构师
This entry was posted in 持续交付 and tagged . Bookmark the permalink.

One Response to 关于DVCS、持续集成和特性分支

  1. Pingback: On DVCS, continuous integration, and feature branches | Continuous Delivery

Leave a Reply

Your email address will not be published. Required fields are marked *