天天看点

发布与分支

首先来说一下发布的概念,发布的是什么呢,是故事(story)吗?不是,是特性(feature)!

特性由一个或多个故事组成,单个故事可以验收,但是不能单独上线。比如当你做完了购物车的故事,是不能把单独把它上线的,因为你无法在购物车中进行结算。从用户体验的角度来说,这两个故事要么一起上,要么都不上。而且也不是一个特性完成了就可以立马上线的,比如圣诞大促的功能做完了,也要等到圣诞节来了才能上。

一种管理特性发布的模式是使用特性分支,对每一个特性都会拉出一个分支,完全开发结束之后再合并回来。比如下面这个图,点击这里查看原文。

发布与分支

然而这种模式是有风险的,martin fowler对这种模式进行了论述。

martin fowler在文章中提到了很多点,这里只强调一点,即多分支下的持续集成问题。假设现在存在三个并行的特性分支,那么加上<code>develop</code>分支就存在四个活跃的分支。持续集成流水线应该去监控哪个分支呢?如果只监控<code>develop</code>分支,其它分支的提交就无法得到持续集成的验证,也就是说大部分情况下开发人员享受不到持续集成的好处。

那么如果每开一个分支就为它建立一个持续集成呢?首先它不是真正的持续集成,因为并不是所有的代码都集成在了一起;其次这么做的代价也会比较大。一个基本的部署流水线应该包括编译、单元测试、打包、功能/集成测试、发布这些步骤,如下图所示:

发布与分支

其中单元测试的环节会包含编译、单元测试和打包;集成测试环节中会运行集成测试;发布环节中会进行部署。如果要对每个分支都建立一套这样的流水线,就会存在很多的重复配置,如果想要更改,则需要改多处;而且每个流水线是独立的,也很难有一个全貌能够让我知道现在都有哪些流水线。我曾经工作过的一个项目使用jenkins做持续集成,因为持续集成配置过于复杂,我们甚至编写了一个脚本调用jenkins的api来复制一整套流水线,如果使用crp来做这件事情的话,就简单多了,我会在下一篇文章会讨论crp中多分支持续集成的配置 。

另一种常见的模式是只有两个分支:<code>develop</code>和<code>release</code>。所有开发人员都在<code>develop</code>上进行开发,项目进行周期性发布。在每次发布之前,开发人员应该完成所有功能,然后从<code>develop</code>上拉出一个<code>release</code>分支,在上面进行测试和bug fix(这些bug fix也应该及时合回到主干)。一旦<code>release</code>分支稳定了,则打个tag,进行发布。

如果线上出现bug要如何处理呢?从上次打的tag拉出一个临时的<code>hotfix</code>分支,修复之后合并到<code>release</code>分支进行发布。

这里也需要管理两个分支,所以持续集成流水线也应该有两套,还是存在重复。但同样的,有了crp对多分支持续集成的支持 ,就可以把这部分成本省掉。

这种模式可以避免分支过多的问题,但也会有两个限制:

需要进行周期性发布。因为所有功能都在一个分支上,不加限制的话,任何时刻总会存在开发到一半的功能。所以必须有一个发布周期,在发布周期开始时做好计划,确定要发布的内容,然后在周期结束之前把计划的功能全都完成才能开始上线流程。如果发布的特性在一个发布周期内完不成,则需要延迟发布。更夸张的情况是如果在圣诞节前半个月就完成了圣诞大促的功能,那么在圣诞节前的这两周内,都不能做任何发布。

需要限制<code>hotfix</code>分支的使用。<code>hotfix</code>只能用来处理一些可以快速修复的线上问题,而对于有一定工作量的紧急需求则不适用,否则就又会出现一个分支上的代码长时间得不到集成的问题。而且如果在一个紧急需求完成之前,再开始做一个紧急需求的话,则又变回了特性分支的模式。

为了解决上述的第一个限制,可以考虑引入特性开关。即每个特性都可以通过简单的修改被隐藏掉,这样所有的特性可以同时存在于主干上,但是又互相不影响对方的发布。

特性开关不仅仅是一个技术问题,更是一个业务问题。比如一个新特性做完了,但是产品经理不确定用户是否需要它,所以需要先发布出去试运行一下。如果发现用户其实不需要它,则需要去除该功能。这个时候,开发人员就需要花费一些时间来仔细地把嵌入在代码库中的与该特性有关的逻辑都去除掉。但如果有了特性开关,就可以快速又安全地关掉这个功能。

特性开关,听起来容易,但其实要做的事情还是很多的。这里只强调两点:

特性开关在发布前默认关闭,这时不仅要保证所有其它的特性(包括没有开关控制的特性和开关已经打开的特性)都是正常工作的。还要保证这个特性没有暴漏出去。

当准备要发布时,会打开开关。这时也要保证在发布之后,如果我关闭开关,能够真正把这个特性隐藏掉,以便能够在需要时快速下掉这个功能。

这两点都需要更多的工作量,具体做法请参看这篇文章。

那么在这种模式下,如果有线上bug要紧急修复该怎么做呢?是否可以直接在主干上进行修复,然后发布呢?理论上来说是可行的的,当然前提是你按照上述文章中的做法对软件的各种开关处在不同状态时进行了充分的测试。如果对已有的自动化测试不够有信心,还是可以保留一个<code>hotfix</code>的分支,从而可以有信心快速对问题进行修复,并上线。

总结一下,分支过多会引入一些问题,而使用特性开关可以避免过多的分支,当然也会引入额外的很多代价。如果你的测试做的充分,就可以潇洒地只使用一个分支;但保留一个<code>hotfix</code>分支可以让你的线上问题修复更加从容。

只保留一个分支不一定是最好的做法,有多个分支也并非一定是坏掉,要根据自己的实际情况作出明智的选择。如果你决定要使用超过一个分支,请参看crp中多分支持续集成的配置。

继续阅读