求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
 
持续集成
 

2011-6-13 来源:infoq

 

持续集成之“分支策略”

现代版本控制系统(SCM)的作用已不仅仅是保存历史版本,它还是各软件开发组织利用其分支功能实现多人并行开发,提高生产效率的一种工具。对于稍有历史的软件产品来说,一般都会有代码分支的出现,也常常见到一些历史悠久的产品其错综复杂的分支版本树甚至将产品交付团队拖入“无尽维护”的泥潭。分支的目的是希望“分而治之”,而持续集成的目的是“频繁集成”,这二者之间又有哪些联系呢?

在《测试三角形与分段构建策略原则》一文中,咱们说到:由于自动化测试时间较长,Joe的团队实施了分阶段的持续集成。虽然这么做引入了一些风险(比如因提交阶段构建中的测试覆盖面小而不能尽早发现代码中问题),但提高了整个团队的开发效率。而且,Joe会根据实际运行情况,在提交构建和次级构建之间不断调整自动化测试用例集来缓解分阶段构建带来的风险。

现在,这个软件游戏平台的第一个版本已经接近完成,马上就要进行内测了。团队面临的问题是:“如何做分支管理?持续集成该怎么做?”

一、短周期发布分支策略

今天是星期五。下班后,Joe和Alice等主要开发人员并没有马上回家,而是在一个小酒吧里聊天呢。

Alice说道:“现在我们一直使用主干开发方式,团队所有人都工作在Trunk上,与之对应的只有一个持续集成环境。下星期就要做内测了,我们是不是应该拉一个测试分支,用于修复测试中发现的缺陷,在主干继续开发新功能呢?一旦修复完内测缺陷的话,我们就可以在这个分支上进行发布,再把这个测试分支的代码变更合并回主干。就像这样。”她拿了一张纸画了出来(如图1所示)。

“好啊,好啊。我们分成两个团队,一个在测试分支上工作,修复内测过程中发现的缺陷;另一个在主干上工作,开发新的功能。”Bob回应道。

“对于拉分支做测试这件事,我没有疑问。但是,我不同意最后再把代码合并到主干上。”Joe说道。“我们一直在使用持续集成实践,目的就是尽早集成。为什么要等到发布以后再将测试分支的代码合并回主干,而不是每次修复一个缺陷就合并回来呢?每次缺陷修复的代码变更不会太多,所以合并起来很容易。等到最后再合并,首先是容易漏掉一些代码,其次是一次合并代码太多,容易出错。所以,我建议下星期拉分支时,为测试分支也建立一个持续集成环境。每次发现缺陷时,都为它写一个测试,加到测试套件中。修复代码提交后,就会触发测试分支对应的持续集成构建。一旦构建成功,就将其合并回主干。”说完之后,他在Alice画的那张图上修改了一下(如图2所示)。

  1. 拉分支之后,开发团队可以继续开发新的功能。而测试团队可以单独对分支进行测试、部署,不受开发团队的影响。
  2. 一旦测试中发现问题,载发人员要在该分支上修复。
  3. 在分支发布之后,一旦发现了严重问题,仅在该发布分支上修复后即可发布补丁版本。
  4. 在分支上做修改后,就要根据实际情况进行分析,是否要合并回主干。如果需要合并,应该立即进行。

“那由谁来负责把发布分支中的Bugfix合并回主干呢?”

“当然是由Bugfix的人来负责了,他是确保合并正确性的关键。如果Trunk上的代码已被修改,无法合并,Bug负责人就要与主干开发人员交流,这个Bug在主干的有效性,然后再决定是否修改,在哪里修改的问题。”

“我们要对发布分支上的Bug定义修复标准,尽量在Trunk上修复Bug,除非这个Bug严重影响发布质量。这样可以避免无休止地在发布分支上做代码修改。这样,Bug数才会收敛,发布分支的活跃期才会缩短。”

“嗯,相对于我们一直使用的主干开发方式来说,这种短发布分支策略的成本是:

  1. 需要多套独立的持续集成环境。即每个分支在处于活跃期时,要有与之对应的一套持续集成环境,以便不受影响。
  2. 每次发布分支上修复缺陷后,只要分支对应的持续集成构建成功,就要将其合并回主干。
  3. 由于主干开发的代码可能因架构改进使原有缺陷不复存在,所以每次合并时都需要人为判断一下合并的必要性。”

二、长周期发布分支策略?

“哦,我之前工作的一家公司,就是用这种分支策略。”Bob说道,“但情况变得非常复杂。版本满天飞,想做合并都不容易。”

Joe说道:“我想,可能是因为他们的客户不想升级版本,所以必须在已发布的版本上再发小版本吧?”

“的确是这样的。”Bob回答道,“他们的发布周期大约是半年。由于已发布的版本质量不佳,所以总是有紧急修复的版本上线。另外,客户比较担心新版本的稳定性,所以只要满足自己的当前需求,就会一直使用旧版本。有些大客户还会要求公司开发针对其自身的特别需求,并快速上线,结果可想而知。”

Alice说道:“其实,这已经是短周期发布分支的变形,即有多个活跃分支的长周期发布分支策略(如图3所示)。这种分支策略是应该尽量避免的,它的复杂性和维护成本都很高,因为:

  1. 每次都要把缺陷修复代码合并到后续的多个发布?分支上,尤其是当该缺陷发生在较老的版本,而当前已有多个活跃版本需要维护时。
  2. 随着时间的推移,每个分支上的自动化测试用例增多,更多的分支会对持续集成环境中的测试机数量的需求快速增加。
  3. 发布周期长诱使团队在已有的发布分支上再做子分支(如图3中的R1.1),这会让集成和验证工作变得更加复杂(如图3中从R1.1到R2.0的CherryPicking操作表明:需要向多个分支上合并部分代码)。
  4. 由于每个活跃分支都要对应一个持续集成环境,因此,分支越多,对持续集成环境的维护成本也就越高。

Bob问道:“有什么办法避免这种糟糕的多活跃分支开发策略吗?”

“办法当然有,但不能解决所有问题。”Joe回答道,“比方说,首先,要确保每个版本的开发质量,让客户放心升级。其次,软件产品要支持自动升级。在通常情况下,只要满足需求,用户就不会轻易升级软件。所以,要让软件具有自动发现新版本并在后台自动升级的能力。当然,在升级后要通知用户。这样,只要将新版本发布到互联网上的某个服务器上就行了。最后,也是最重要的一点,新版本发布周期要短一些,不断快速地推出新特性,这样就可以让用户对产品及研发团队有信心,让客户感觉他们的需求很快就会被满足。”

“对于那些企业用户来说,这种方法可能不管用。因为,企业内网很少可以连通外网。”Alice说道。

“如果是这种情况的话,除了软件本身质量好且能自动无缝升级以外,在销售时可以与客户签订协议,告知所售软件版本的生命周期(比如18个月)以及升级条款,促使企业升级该软件,比如免费的大版本升级,或者因缺陷原因可免费升级等等。”Joe回答道。

“嗯,我们开发的是游戏软件平台,部署在互联网上,所以不会遇到这个问题。”Alice说道。

Joe微笑着说道:“我们将会面临另外一种问题,即多个小团队开发不同的游戏组件问题。”

“哦,对了!现在我们的游戏平台中虽然仅有几个游戏,目前还一起在主干上开发。但在下一版本中,我们会增加大量的游戏组件,那应该如何应对?我们的持续集成环境应该是什么样的呢?”Alice大声地问道。

“嗯,是个好问题!”Joe回答道。“我已经有了一些想法。我们内测结束后,再详细讨论吧!时间也不早啦,大家回去休息吧,周末愉快!”

持续集成之“分支策略”(续)

在前面,咱们谈到生命周期长短不同的两种分支策略。对于不超过二十人的小团队来说,推荐使用短生命周期的分支策略。Joe的团队在首次发布之前,也一直使用这种方式。然而,首次发布之后,因市场反响非常好,公司决定加大开发投入,希望更快地推出升级平台,以及更多基于平台的游戏。

一、按特性分支的持续集成策略

现在,Joe的团队中,开发人员快速增加,已接近30人了。由于首次发布后的市场压力,大家一直在赶进度,持续集成的失败频率越来越高,修复构建的时间也越来越长,排队等待提交的代码也越积越多。“这种状况不能再持续下去了,需要想个办法解决它。”Joe决定下午召集主要人员开会,分析一下原因和对策。

“现在我们还在使用提交令牌(参见《Checkin Dance》一文的最后一节),可我们的开发人数已经翻了一倍。而且,我们自动化测试用例的数量也激增。”Joe说道,“有时候我想提交代码都要排队等很长时间。”

“嗯,每天等待提交的人也挺多的。”Alice说道,“现在看来,虽然持续集成让我们每次提交的质量都更有保证,但是在同一个主干上开发的人数太多,它就成了一个提高开发效率的瓶颈了。”

“要不这样吧:我们把大家分成小组,每个小组从主干上拉出一个分支,完成一组相近特性的开发后,再合并回主干。”Bob边说边在白板上画了出来(如图1所示)。

“对应的持续集成方案也需要调整。包括:

  • 保留现有主干对应的持续集成平台,但不许在主干上直接开发代码;
  • 每个分支增加一个相对应的持续集成平台;
  • 每个分支的持续集成平台构建中需要包括该分支对应特性的单元测试、功能测试;
  • 每次向主干合并时,都会触发主干上的持续集成,构建中应包含整个系统的单元测试、功能测试等。

这样,每个小组的人数不会太多,提交时需要等待他们提交完成的概率应该不会太大。另外,每个分支的持续集成上只运行自己分支对应特性的单元测试和功能测试,这样,构建时间也会缩短。”

“听上去是个好办法,”Alice答道,“可是,我对这个方案有几个疑问。比如说,这几个小组在什么时候做同步?每个小组什么时候向主干合并代码?”

“嗯,好问题。我还没有想到这么多呢。”Bob皱了皱眉,感到很沮丧。

Joe笑了笑,说道:“的确是不错的方案。只要加一点同步与合并规则,改进一下。”然后,他拿起白板笔,在图上加了几笔(所图2所示)。

“规则如下:

  • 每个小功能在尽可能短的时间里开发且测试完成,最好是在一周之内。
  • 每组做完一个小功能后,一旦该分支上的持续集成构建通过,而且手工验证没有问题,就可以向主干合并代码。
  • 合并后,与主干对应的持续集成平台会立即验证这些代码。
  • 如果主干持续集成平台的构建失败,那么是哪个小组提交导致的,就由哪个小组负责修复。
  • 每天各组在开始工作之前,都要将主干上那个最新且通过主干持续集成构建成功的代码检出,并与各自分支的代码进行合并。

其实,这就是小组级别的“Checkin Dance”。目的还是要持续集成,即尽要将各小组的工作成果集成在一起。如果每个小组能够做到频繁与主干代码同步的”

Alice问道:“由于每个分支上都是多人开发,那么当某个功能完成后,并需要合并回主干时,该分支上可能已经有一些代码是属于尚未完成功能的代码。我们需要把属于该功能的代码修改挑选出来后提交到主干吗?”

“你是说Cherry Picking吧。只要我们能够通过技术手段确保用户无法访问到未完成的功能,就不需要Cherry Picking了。比如通过配置项或功能开关的方式。”Joe说道。

“这样做,听起来挺好的,但还有一个问题需要解决,那就是:现在大家的代码耦合度太高啦。每增加一个小功能,都要修改很多个位置的代码。”?Bob说道,“如果这么做的话,各组之间的代码冲突会很多,合并可能带来很多问题。”

“的确是这样的,目前的持续集成方案只能缓解合并问题,但无法解决合并中的代码冲突问题,只有通过对代码的结构进行调整才能够解决。”Job说道。“而且,对于我们这样的软件系统来说,对架构进行调整带来的益处更大。”

二、模块化应用程序的持续集成

“啊哈!架构调整?”Bob笑道,“架构这个词让人用得太滥了,还是不要提的好。一提到架构调整,我就想起在前一雇主公司干的活了——每次架构调整都是重写代码。”

“哦,事实上,我们系统的架构基本上是模块化的,比如平台与具体游戏之间的边界还算清晰。”Joe回应道,“现在我们所要做的是强化模块化。因为,新加入的开发人员对系统了解不够深入,有些功能的耦合度开始增高了。我希望每个游戏就作为一个独立模块,进行开发与测试。而它所依赖的游戏平台需要提供稳定的对外接口。”

Alice说道:“那我们就可以不用前面提到的特性分支策略了,只要把每个模块做为一个独立的代码库进行开发,将它所依赖的游戏平台作为外部依赖进行集成就行了。”

“的确是这样的。”Joe肯定的回答道。“如果每个模块对外都有某种形式的接口(比如API,接口定义文件),而所有外部依赖都通过这些接口与其进行交互的话,就可以这样做。”如图3所示。

“如果这么做的话,我们的持续集成方案应该是什么样的呢?”Bob问道。

“那不是一样嘛,即然都是独立的,各模块做各自的持续集成不就行了嘛。”Alice说道。

“当然不行,因为这些模块之间仍旧需要通过彼此交互才能正常运行起来,尤其是对于那些有信息交换的游戏模块,集成测试就更加重要。”Joe回答道,“既然需要集成,就要做持续集成。”

Alice问:“那我们有这么多个游戏,每个游戏都要与基础游戏平台进行持续集成,到底应该怎么做呢?”

“我们可以这么做。”Bob拿起笔在白板上画了起来(如图4所示)。“为每个模块的代码库建立对应的持续集成环境,包括每个游戏和基础平台。无论哪个模块代码库修改了代码,都会触发对应的持续集成构建,一旦该模块的持续集成构建成功以后,就会触发一个包含所有游戏和平台的集成构建。”

“这样不错,但是现在每个模块都对应独立的代码库了,那么在最后各模块集成构建时,到底用各模块的哪个版本呢?”Alice问道。

Joe说道,“Alice的问题非常好。在最后各模块集成构建时,除了那个主动触发构建的模块使用最新版本外,其它模块都使用最后一次令该集成构建成功的那个对应版本。”Joe边说边在白板上画了一个例子。

“比如,对于我们目前的系统来说,一共有四个游戏模块和一个基础平台。假如最后一次成功的集成构建中,各模块对应的版本分别是123,245,212,467 和12387。当我们对游戏模块A进行了一次提交,其版本变为124,并且通过了它自己的持续集成构建以后,就会触发最后的集成构建。这次集成构建所对应的各模块版本分别为124,245,212,467和12387。如果这次构建成功,则下次最后集成构建就以这些版本为基础;如果这次构建失败了,则标记游戏模块A的124版本是可疑版本,尽管它通过了其自身模块的构建。同时需要有人对这次集成构建进行分析,进行问题定位并修复。”如图5所示。

“那么,我们的基础游戏平台也是由多个模块组成的。我们是否也需要把这些模块独立成库,使用同样的方式进行持续集成呢?”Bob问道。

Joe回答道:“我认为现在还不需要。平台内部模块化是应该的,但因为它自身的构建时间并不长,还没有必要独立成库。”

Alice此时说道:“这样看来,我们的持续集成问题可以按这种方案来解决。让我们试试吧。”

那么,Joe的团队使用这种持续集成方案以后,还会遇到什么情况呢?比如,基础平台的构建时间变长,会怎么样呢?

需要注意的是,无论采用哪种方法,我建议都不要让同一组人一直工作在一个模块上(虽然这是在各组织中经常见到的),而是让一组人工作在一组模块或功能上,并让小组成员在各组间流动。这样有利于组间的知道共享,对保持架构的一致性也会起到积极作用。

持续集成之“依赖管理”

在前面,我们讨论了多组件应用程序的持续集成策略,即:为相对独立的组件创建自己专属的代码库,然后通过现代持续集成工具进行组件间的持续集成。Joe的团队在首次发布之后,开始使用这种方式。然而,没有多久,他们就遇到了一个问题:一次提交构建所花费的时间太长。

一天,Joe就早早地来到了办公室。因为他前一天下班前,他开发的用户故事还有一小点就完事儿了。他想利用早上这点儿时间把它搞完,交给测试人员进行测试。他修改了某个模块的一段代码,在本地构建测试通过以后,就提交了, 然后起身去楼下买些早点。十五分钟后,他回到了电脑前,令他沮丧的是,这次构建还在进行最后的阶段,即所有模块集成测试和系统级测试。他只好又起身去冲了杯咖啡。然后,一边看着屏幕上的构建进度条,一边喝着咖啡。七分钟后,构建终于成功结束了。虽然这是一次成功的构建,但总是觉得不爽,花了二十多分钟才做完提交构建。于是,他开始仔细地查看起构建脚本和构建日志。

一、一次生成,多次复用

中午吃过午饭,他把Bob和Alice叫到一起,开始讨论早上他遇到的问题。

“的确是非常烦人,现在构建时间太长了。”Alice说道。

“我今天早上查看了一下我们的构建日志,发现构建时间长的原因之一是:每种测试开始之前都要更新代码,再重新编译一次。”Joe说道。

Bob提出了一个解决方案,并画在了白板上。“我们是否可以建立统一的产物库,每次构建的产物都以一定的规则放在其中?这样,后续的测试需要使用这些二进制产物的话,直接从产物库中获取即可。”(如图1所示)

“听上去不错。然而,我们是否需要把每次构建中产生的内容都放入产物库,这会非常快地吃掉我们的磁盘空间。”Alice不无担心的说。

“目前构建完成以后,所有的产物都放在那台构建机器上。我们也遇到过因构建机器硬件问题或误操作将所有重要历史信息都丢失的事情。所以,我们至少需要备份。”Bob回答道,“另外,将每次构建的产物放在统一产物库中,我们就可以解决Joe刚才提出的重复编译问题。当然,我们需要有选择地将重要的构建产物放到统一产物库中,而不是所有内容。通过在每次构建后增加一个上传任务,让各小组将其认为有用的信息上传到产物库,比如构建日志、测试报告、构建后的二进制文件等。但一些临时文件就没有必要了。当然,这只能缓解产物库膨胀的速度。尽管持续构建的次数非常多,但我们并不是需要一直保持所有构建的产物,所以,可以定期删除那么没有保留价值的构建产物,比如对那些重要构建的产物进行标记,其它的就可以删除了。”

Alice和Joe都点了点头,表示同意。但Joe的眉头马上又皱了起来。“嗯,好象这里还有点儿问题。”

“什么问题?”Alice和Bob同时问道。

Joe说道:“对于我们平台中的一些小游戏组件来说,这没有什么问题。因为它们的构建产物都不太大,网络传输带宽和速度都不是问题。但是,对于那些很大的二进制文件或测试数据来说,这么做的话,可能就有问题了。”大家都点了点头,并开始思考这个问题。

忽然,Joe叫道:“不好意思,其实这不是个真正的问题。首先,我们的测试数据变化就不频繁,原来也没有放在产物库中,而是放在了一个共享目录中进行版本管理。所以,这部分在构建中的做法与之前没有什么不同。其次,对于较大的二进制文件,只要在需要它的构建机器上把它缓存起来。那么在下一次构建时,构建脚本可以对这个本地版本进行验证,如果版本正确且没有被破坏(比如通过MD5验证)就可以继续使用。否则,就再从统一产品库取出正确的文件将其覆盖就行了。”

“这么做还有一个好处,而且是非常重要的好处。”Alice补充道,“我们的手工测试版本也可以从统一的产物库中拿到,这就保证了自动化测试所有的二进制文件与部署到手工测试环境中的二进制文件是同一个文件了,也就不会出现因重新编译时的环境不同而导致的不一致问题了。而当我们做上线部署时,也从这个统一产品库中获取,从而做到自编译开始直到上线部署的二进制包的一致性啦。”

于是,Joe与团队一起对其持续集成平台和所有构建进行了改造,将其打造成了一个具有组织级产物库的持续集成和发布管理平台。他们不但有效地缩短了每次构建的时间,还可以轻松地通过产物库追踪到每个上线版本在代码版本控制库中的对应代码,让问题追查变得更容易了。

二、依赖管理

一个月后,根据市场的需求反馈,他们开发的一个游戏升级了,反应速度非常快,效果非常好。但引申出来的一个问题是:游戏和平台的升级频率不一致,持续集成应该怎么做。对于Joe的团队来说,是一个非常大的问题,因为他们的开发流程严重地依赖于持续集成平台。于是,Joe和团队的核心成员打算讨论一下,如何应对目前这种情况。

在会议室的白板前,Joe画出了当前所用的持续集成策略(如前图所示)。

Bob说道:“到目前为止,我们已经发布了几次,而且最近一次只发布了一个游戏应用。我们如何管理我们的发布流程呢?在我之前工作过的公司中,产品会有几个版本,包括稳定版本、已对外发布或即将发布的版本、最新版本:用于公司内部测试。每当将要发布新版本时,就拉出一个分支,进行内部测试,并修复严重的缺陷。当没有严重缺陷时,才能作为稳定版本公开发布。”

Alice答道:“对于单个的软件交付产品来说,通常可以通过“按发布拉分支” 的方式进行开发,正如我们最开始所使用的持续集成策略。但是,现在我们的游戏平台与单个交付产品不同。我们有自己的服务器集群,只要测试覆盖率及测试质量足够好,测试速度足够快,我们就可以通过小流量试验部署后再大规模上线的方式进行发布。现在,我们的问题是由于各个游戏组件的发布频率各不相同,组件存在依赖关系,导致很难决定在持续集成过程中,到底应该使用哪个依赖版本。尤其是我们现在还有一个公共库,被多个组件使用。”

Joe说道:“我们先梳理一下整个平台上的依赖关系吧。通常来说,软件中的依赖关系通常包括编译时依赖、测试时依赖和运行时依赖。而从依赖形式上可以分为库依赖和组件依赖。所谓库依赖,是指依赖于那些不受控的库文件,比如我们使用了一些开源或者付费的的类库文件或工具,这些库文件的特点是更新较慢,甚至基本不需要更新。而组件依赖是指依赖于那些由自己团队或公司内的其它团队开发的组件,这类依赖的特点是更新频率相对高,有些甚至非常频繁。对于库文件依赖,我们可以在代码库中建立一个目录,叫做lib,并在其下建立build、test、run三个子目录,把我们所依赖的库文件放到相应的子目录中。同时,每个库文件的文件名中最好包含它的版本号,如nunit-2.6.0.11089.bin。这样,就很容易看出依赖了哪些库文件。”

Bob接道:“可惜我们不是用Java平台,否则我们可以用象Maven或Ivy这样的工具来管理这些外部库依赖了。而且,同时可以在公司内部利用Artifactory或Nexus这样的开源工具建立一个内部统一服务器,专门管理公司内部所用的这些库依赖。”

Alice说道:“我们也可以自己做一个简单的依赖管理系统。比如使用Key-value的格式用文本文件来描述所用到的库文件名及版本号及存放位置,然后再写个通用脚本读取信息下载到本地使用。”

Bob接着问道:“对于这种库文件的依赖管理相对容易一些。而我们面临的重要问题好象是组件依赖管理。有什么好办法吗?”

Joe想了想,说道:“方法倒是有几个,各有优缺点。一种方法是将组件依赖转成库依赖。其适用的场景是该组件经过一段时间的开发的维护后已趋于稳定,变化不太多。此时就可以将这个组件打包后与其它外部依赖库放在一起,并加入正确的描述,以便依赖于它的所有组件都可以正确地拿到正确的版本。还有一种方法是我们目前所用的方法。即每个组件各自进行持续构建,然后再做集成构建。其中存在的问题是我们如何管理各组件不同版本之间的组合关系。我们一直使用的策略是无论哪次提交,都会触发整个构建。目前要做的有两件事:一是将公共库独立出来,进行单独构建,并且一旦构建成功,自动触发那些依赖于它的其它组件构建,最后进行集成构建。只要我们记录每次构建后的版本及源代码的 revision就行,以便可以追踪。二是将游戏平台的持续构建触发其它游戏组件的持续集成。所以,触发关系应该是这样的。”Joe拿起笔,在白板上重新画了一下触发关系图(图2)。

Bob摇了摇头,说道:“这样还是解决不了我们之前说过的问题,即我们的发布频率不一致,如何来管理这些发布之间的关系。”

“噢,这个问题是这样的。”Joe回答道:“我认为,我们之前单独发布一个游戏组件是不对的。我们因市场压力而将该游戏组件直接部署到生产环境中,尽管在发布前的评估认为,该游戏所依赖的平台接口没有发生变化。正确的做法有两种:(方案A)将平台作为一个整体一同发布,因为我们对平台也做了修改,当时,所有的持续集成测试都是基于主干的最新版本所做的。(方案B)让所有游戏组件依赖于游戏平台的最新发布的稳定版本进行开发。由于平台的新功能开发较慢,所以只要平台接口不发生变更,各游戏应用都可以基于平台的稳定发布版本进行快速更新。但只要某个游戏需要修改平台的接口,就必须与平台的最新代码进行持续集成,并一同发布。”

Alice皱了皱眉,说道:“这么看来,对于整个软件来说,能够保持主干随时可以发布才更容易管理组件依赖。因为每当需要发布时,直接做主干发布就行了。实在不行的话,只要将所有组件在同一时间点拉出一个发布分支,然后统一上线就行了。”

Bob说道:“这样也有问题。我们的部署会很麻烦,时间可能会很长。”

Joe笑着说:“部署麻烦,我们可以通过一系统列的自动化操作来解决。部署时间长的话,我们使用的是集群部署,因此可以采用分批替换的方式来部署。但这种发布方式给我们带来的益处是可以很快的响应市场需求。”

Joe拿起杯子喝了口咖啡,接着说道:“当然,这对我们的开发工作也提出了挑战。我们必须使用多种手段才能做到主干持续可发布状态。比如(1)将新功能隐蔽起来,直到它完成为止;(2)把所有的变更都变成一次次非常小的增量式修改,每个修改都做到可发布;(3)通过抽象达到分支的目的(Branch by Abstraction)。另外,我们的自动化测试也需要保持在较高的覆盖率,并丰富其它类型的自动化测试,比如性能测试,压力测试等。如果遇到特殊情况,我们再坐下来商量对策。”

Bob仍旧有点迟疑,“这样可能会增加我们的开发成本。不过,可以试一下,看看效果如何。”

于是,整个团队开始行动起来了。他们在这条道路上还会遇到什么情况呢?让时间来回答这个问题吧。



集成与构建指南
项目管理:Maven让事情变得简单
持续集成工具hudson
持续集成
Maven权威指南
程序集(UML中的包)之间循环
更多...   


产品发布管理
配置管理方法、实践、工具
多层次集成配置管理
使用CC与CQ进行项目实践
CVS与配置管理
Subversion管理员


海航股份 重构及持续集成
电研华源 设计原理、建模与重构
软件配置管理日构建及持续集成
单元测试、重构及持续集成
中国软件研发中心 单元测试与重构
单元测试、重构和持续集成实践
罗克韦尔 C++单元测试+重构+Gtest
更多...