讀過了Perforce官方的mainline model的文件,又看到Google與Facebook都使用TBD,以及我自己在開發上遇到的問題,讓我想看看TBD到底如何可以幫助我們解決這些工程上的問題,看起來作者非常反對feature branch,而我自己親身經歷的感受也的確,要開feature branch,除非能做到Perforce官方推薦的經營方式,不然不只Merge會有災難,開發時也是災難連連。以人性角度來說,假設我做componentA,如果有10個feature branch都有componentA,那每個branch有問題我都要去看,我修了一個componentA的問題,由於每個branch分支出去的時間點不同,其它branch有的可能有,有的可能沒有這個問題,那我怎辦,只能等著人家來報問題,那我的時間很多都花在解這些Branch的問題上。如果採用的是TBD的概念,我只要保證trunk沒問題就可以了。或許在code撰寫方式上需要花很多工,但是我只需一次工,也可以將焦點集中在一個地方。對於我這種普通人,這是比較人性化的工作方式。
甚麼是TBD
- 一個軟體開發的分支模型,也被稱作mainline
- 同一個產品開發的所有人員共享一個Repository,有一個trunk,單一Developer或是Developer團隊可以有自己的private branch,所有修改最後都會回到主幹
- 只有在Release時才會有官方的分支,一般Developer不能對Release Branch作動作,只有Release Engineer可以更動Release Branch,當Release Branch完成它的任務,就會被砍掉。
- Google與Facebook都採用這種分支模型
有需要Release才Branch
- Release之後的branch,就不會有大的更動,只有Release Engineer會進行將挑選Commit合併到Release Branch的動作
- 多一個Release Engineer的帽子
- Bug先在trunk修好,之後把Commit合併到Release Branch,而不是在Release Branch修好再整合到trunk,這樣可以把修改Release Branch的人限制在最小程度。
Developer的責任
- 每個Developer都要保證Build會成功
- Google與Facebook在新進員工訓練下很多工夫在這上面。一開始沒生產沒關係,但是不要讓公司產品Build不出來!
- Rollback/revert是最後不得已的策略
- 複雜產品或是大公司都會有一堆Pre-Commit認證。
- Developer應該養成習慣,證明Commit是沒問題的:
- Commit之前把Code更新到最新
- 以最新的狀態將整個產品重Build一次
- 確認更改到的功能無誤(當然關聯的功能也要確認一下)
- Commit,總算搞定,休息一下
當某個功能花太長時間才能開發完
- 使用Branch By Abstraction (2013重提)
- 避免Branch到處開,最後整合不回來
什麼不是TBD
TBD Quick cheklist
- Developer幾乎只commit到單一trunk
- Release Engineer創建Release Branches, 幾乎只把Commit整合到Release Branch
- 用「幾乎只」來形容是因為如果bug無法在trunk重現(有可能是相關的code已被改變),那Developer就要在Release Branch上修正,然後把Commit整合到trunk。
如果應用Release Branch的概念,請記住:
- TBD代表Developer不能Commit到Release Branch
- TBD代表你將會刪除不再使用的Release Branches,不會做任何整合回trunk的動作
Developer需要Commit到多個Branch
- 當然不是TBD
- 如果想要Branch,請使用Branch by Abstraction
- 老鳥總是會說有Special Case需要Branch
- 重點就是合併的複雜度,10個Branch,每個人都在那邊Commit來Commit去,有些相關,有些不相關,有些Commit到2個Branch,有些Commit到1個Branch。這我超有感,我們團隊面臨到的狀況正是如此。
- To Branch or Not to Branch?這是已經被爭論許久的問題。
- 作者說除了Release Branch之外,不應該有任何Branch在共用的Repository上,但是Developer或是Developer團隊可以有自己的Private Branch。
- 就算Feature需要花很長的時間做而且沒時間花在整合上,還是不應該Branch by Feature,應該使用Branch by Abstraction。我們團隊遇到大Feature就會開一個Branch,由於這種Featrue Branch沒人經營,Feature開發中後期,就會花很多時間在整合,而複雜度隨著Commit數量增加越來越複雜,最後只要提到要整合回mainline,每個人的態度都是把一切交給命運。
沒有在Branch上作持續整合
- 不是TBD
- 很多Open Source的Developer聲稱沒有持續整合也不會怎樣,作者建議有10個以上的Developer就應該要做持續整合。個人認為就算1個也應該做,誰敢說自己完全不會改壞自己的Code。
手動管理Component Dependency版號
- 對外來的Component,通常都是用外面已經Build好的穩定版本,版號是固定的,可以直接寫Build管理檔案內(例如makefile,Maven的pom.xml)
- 對於內部的Component,自己手動指定該Build所需的Component版號(Ex:1.1.2之類),很容易造成不知到哪個版本的產品該用哪個版本的Component,要嘛就是把Code拉近Product Dir裡面全部Compnent都Build新的,要嘛就是根據Perforce或SVN的Revision Number,或是用Jenkins產的Build Number。手動是複雜度的地獄。
範例:
Perforce或SVN的架構
trunk
releasecomponent1component2component3component4productAproductB
private
productA用到component1,component3
productB用到component2,component4
要Build productA,CI可以先build component1,component2,然後build productA,但因為可能component1不斷開發,已經到了reversion=1500,而productA不需要reversion=1234後續開發的功能,就可選擇從component1的reversion=1234抓code過來build。或是直接把compoent1的reversion=1234的code放到到productA底下,目錄架構就會變成
trunk
component1
component2
productA
component1
component2
在Perforce有Module這個概念可以應用,History也會留存。
CI不是從Root開始Build
- CI在Build所有的Component都應該重新開始Build,不可以有任何的快取,或是已經Build好的Component,因為這樣無法反應code的最新狀態。
用詞不當
Mainline意指其他事物
基本上Mainline就是指TBD,不過在1993年的ClearCase,它的mainline長的如下圖:
這是一個非常花時間的Branch Model,它的精神就是最後才整合,與TBD的早期整合正好相反。
上圖的劇本:
- mainline開發一段時間,Branch出1.1.x
- mainline繼續開發,1.1.x也繼續開發
- 接下來1.1.0要Release,即將合併回mainle,maineline因為要開發1.2.x,害怕1.1.0整合進來會很亂,所以先Branch出1.2。
- 1.1.x功能告一段落,1.1.0Release,此時合併回mainline,由於mainline的code已經不太相同,合併就是災難。
- 1.2繼續開發,mainline繼續處理混亂狀態
- 1.1.1Release,因為1.2需要有1.1的功能,所以又要合併回mainline,剛處理好混亂狀態的mainline要再處理一次混亂
- mainline處理完混亂,開始合併到1.2.x,因為兩個branch長得又不太一樣,所以又是災難
- 1.2處理完mainline下來的混亂之後,終於可以Release 1.2.0
可以看到,每次合併都是一場災難,而這個災難的次數還真不少。其實我們也是使用這個方式,由於有兩個以上的新版本同時開發,branch出去,branch執行有問題也不知道找誰修,要再回到mainline又是一堆工,雖然Branch by Abstraction不見得是萬靈丹,但作者提出的問題我已經有親身體會。
Feature Toggle
Martin Flower歸納出來的名詞,這個技巧是對一些已實作或是實作中,但還不想開放的功能,目前有些人以為這是與TBD一起用的,其實不然,這招早就存在,分為以下兩種
- Toggles at runtime,執行時期判斷旗標,看要不要開放此功能。
- Toggles at build time,建置時間判斷建置參數,看要不要把功能相關程式碼build進去。
不管如何,CI Server可以很好地對應這種需求
Branching is not the problem, merging is the problem
這就是TBD所想要解決的問題,Branch很方便,不是毒蛇猛獸,但是要如何管理好Branch,就是軟體工程的奧妙之處。