基于主干的开发(TBD) Trunk Based Development


原文链接: 基于主干的开发(TBD) Trunk Based Development

Trunk Based Development,缩写为TBD,中文就是基于主干的开发。

什么是TBD,无需太多文字,看下图即可

有人反映看不大懂,好吧,我懒得码字,引用一段TBD的说明文字(来源http://nedwu13.blogspot.kr/2014/01/tbd-what-is-trunk-based-development.html),下面这三句话是关键点:

同一个产品开发的所有人员共享一个Repository,有一个trunk,单一Developer或是Developer团队可以有自己的private branch,所有修改最后都会回到主干
只有在Release时才会有官方的分支,一般Developer不能对Release Branch作动作,只有Release Engineer可以更动Release Branch,当Release Branch完成它的任务,就会被砍掉
Bug先在trunk修好,之後把Commit合併到Release Branch,而不是在Release Branch修好再整合到trunk,這樣可以把修改Release Branch的人限制在最小程度。

感谢nedwu13的翻译。

版本管理策略无非两种:基于主干的Trunk Based和基于分支的Feature Branching,而Feature Branching又可以细分为Release Feature Branching、Integration Feature Branching和Build Feature Branching,不管哪种Feature Branching,在DevOps文化里,皆为妖魔鬼怪,存在的主要问题为:

一个功能模块,或者说是Feature,会存在多个分支上,那么好了,想改点东西,先要知道这个Feature存在在哪几个分支上,然后挨个看一遍,哪个需要改、哪个不需要改,要改的东西可能还不一样,改完了还要挨个测试一遍
合并困难,那么多分支,分支到主干、主干到分支,多路双向合并,想想都头大,所以一般就不合并了,挨个改一遍好了

三种基于分支的版本管理策略详解,参见链接 http://www.alwaysagileconsulting.com/articles/version-control-strategies/

讀過了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

component1
component2
component3
component4
productA
productB

release
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,就是軟體工程的奧妙之處。

`