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

作者: 喬梁  來源: InfoQ  發布時間: 2011-04-12 10:42  閱讀: 2521 次  推薦: 1   原文鏈接   [收藏]  

  在前文中,咱們談到生命周期長短不同的兩種分支策略。對于不超過二十人的小團隊來說,推薦使用短生命周期的分支策略。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的團隊使用這種持續集成方案以后,還會遇到什么情況呢?比如,基礎平臺的構建時間變長,會怎么樣呢?

  需要注意的是,無論采用哪種方法,我建議都不要讓同一組人一直工作在一個模塊上(雖然這是在各組織中經常見到的),而是讓一組人工作在一組模塊或功能上,并讓小組成員在各組間流動。這樣有利于組間的知道共享,對保持架構的一致性也會起到積極作用。

1
0
 
標簽:持續集成
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()