1. 程式人生 > 其它 >4種常見分支模式解析及優劣對比

4種常見分支模式解析及優劣對比

簡介:團隊研發的本質並不是團隊規模越大,研發的效率就越高。我們以為團隊規模越大,研發效率就會越高,可以做越多的東西,但是我們發現團隊規模大到一定程度,整個研發效率是會下降的,甚至降得非常快。為什麼團隊的規模越來越大,我們的釋出反而越來越慢了?

策劃&編輯|雅純

團隊研發的本質

我們曾經接觸到一家企業,它一開始只有8個人,那個時候每個月都可以發一兩個版本出去,客戶都可以用到,因為他們是做醫院的資訊管理HIS系統。他們覺得做得還不錯。後來團隊發展比較快,規模到了80人左右,卻半年沒發一個版本。這導致實施團隊沒臉見客戶,因為客戶說半年前提的需求怎麼還發不出來。

這個時候悖論就來了:我們以為團隊規模越大,研發效率就會越高,可以做越多的東西,但是我們發現團隊規模大到一定程度,整個研發效率是會下降的,甚至降得非常快。

站在團隊的角度來說,因為人多,協作越來越慢,協作的成本也越來越高。我們發現團隊的研發模式,人越多越會有問題,因為衝突更多,等待更多。這裡衝突是指程式碼整合釋出過程中的衝突,而等待也是整合和開發過程中程式碼彼此的等待。

以下是兩個具體的場景。

假設有兩個程式設計師A和B一起工作,A一開始每次提交都把工作逐漸成功提交到線上去,然後B提交了一個版本,導致編譯失敗了。這時,A就無法提交,因為提交就會掛,要等待B修復問題才能提交,這時A的提交和B的工作就產生了衝突。

第二種情況,多個分支往同一個分支合併,FeatureA先合進主幹,FeatureB晚了一點結果發現無法合併,因為基線不一樣了,這時候必須先解決掉程式碼衝突才能合進去。

如上圖,假設現在有3個人,A、B和測試C,每個人的點代表它做的任務,比如A一直在做自己的事情,每完成一個事情就開始做下一個事情,做完第三個事情的時候他覺得需要去找B聯調一下,就給B發了一個ping,但是B有自己的節奏,在忙他自己的任務,所以並未馬上響應A的請求。他發現有一個任務可以提測了,他就告訴了C,C發現有問題就馬上Pong了回去,但是這時B在忙另外一個任務,沒有響應。C發現B無響應,又發了一次Pong,這時B看到了A和C的訊息,他先處理了A的事情,給A回覆了一個Pong的訊息。

我們發現,程式設計師和程式設計師,測試人員和開發人員之間,在整個的開發協作中其實是非同步的、延遲協作的過程。每個人並不是收到一個請求就馬上回復,馬上協作,往往都是有自己的步調和自己的動作,可能會產生延遲。所以當產品更復雜,協作更多,團隊更復雜,團隊的人多了以後,協作成本就會快速上升。

在這樣一個非同步的、延遲協作的過程中,程式設計師面對日常開發的工作,需要有一套相應的研發模式,來保證在協作過程中能夠持續地把資訊同步掉,並快速地響應掉。

軟體交付過程,本質是開發者圍繞程式碼庫的協作過程。無論是產品程式碼、配置、環境和釋出流程,都可以通過程式碼來描述,並儲存到程式碼庫裡。

因此,研發模式的目的就是約束我們在圍繞程式碼庫工作時的行為,本質是一種圍繞程式碼庫的行為約束。

研發模式我們狹義地理解為分支模式,包含一系列的行為約束,比如分支型別及其標識、分支的生命週期、Commit在分支間的流轉方式,以及流轉的約束條件,還有分支和程式碼之間的對應關係等。接下來我們會一一探討。

研發模式是一系列研發行為的約束,目標是避免衝突、減少等待。在協作的過程中,人多了之後帶來的最大的問題就是衝突變多、等待變多,所以好的研發模式應該儘可能的避免衝突,儘可能的減少等待。

首先看一下研發模式和研發行為之間的對應關係。

這些研發行為和程式碼庫行為有一個Mapping(對映)關係。開始新的特性開發時,我們會建立一個新的特性分支。做一次程式碼的提交整合,其實就是一次Commit和Push,完成之後進入整合驗證,就做了一次分支的Merge。

同樣地,整合完進入待發布也是在做Merge,而完成釋出意味著打一個Tag。程式碼庫裡的操作記錄了我們的研發行為,所以研發行為和程式碼庫的操作可以做到一一對映。

要避免衝突,唯一的方法是大家彼此隔離,分開就沒有衝突。在程式碼庫裡面,很多時候通過分支的方式,來做工作之間的隔離,避免衝突。

要減少等待,而等待是資訊不同步造成的,儘可能地做到資訊同步,就不用等待。在程式碼裡面的等待,是程式碼之間基線的同步,比如說頻繁地提交。所以其實分支是用來避免衝突和做工作隔離,而頻繁地提交合並是為了做資訊同步,減少等待。

Q:如果是一個人做軟體開發,用什麼樣的分支模式?一個人會不會有衝突?

一個人做軟體開發的時候是不會有衝突的,一個人工作的時候不需要很多分支,一個分支就足夠。一個人做開發,也不用等待資訊,因此可以一條主幹走到底。但是如果人數擴張到10人、100人,彼此之間就會有工作的隔離,彼此之間也會存在著衝突,也存在著等待。所以在這個過程中,隨著協作的人數越來越多,分支的模式會不斷地發生變化。

4種常見分支模式解析

主幹開發

團隊人很少(比如1~2個人)的時候,最常見的研發模式是Trunk—BasedDevelopment,也叫主幹開發方式。

主幹開發方式一條主幹分支走到底,開發的過程中不會有太多的衝突,要求程式碼持續整合到主幹上去,所以在開發過程中不需要做相應工作的隔離。開發的過程中,所有的開發者在主幹上面頻繁地提交,頻繁地整合。這種分支模式下,唯一的分叉出現在釋出的時候,為了能夠把釋出版本隔離出來,有了釋出分支。

這種模式下,不需要做分支隔離,資訊同步通過持續頻繁地提交來保證。在人數比較少,並且整個工程能力比較強的時候,這是我們推薦的研發模式。

但是當參與開發的人數越來越多時,主幹開發的衝突機率就大大增加了,對工程能力的要求也越來越高。

所以說主幹開發不是萬能藥,主幹上的人越多,程式碼提交的衝突機率就越大,而且解決衝突的風險也越大。如果兩個人的時候,即便有衝突我知道只是和另外一個人有衝突,如果是10個人,這中間就會產生很多的問題。

另外在主幹開發裡面,要保持資訊地同步,需要做頻繁持續地提交,而且每次提交的力度要很小,這針對有一些特性來說,可能只做了一半,這時需要將它提交上去,需要通過特性開關等方式來進行隔離。比如說這個是還未完成的特性,提前把它的開關製成Off,再做相應的提交,但是特性開關本質上也是一個分支。

特性開關只是用程式碼的形式拉了一個分支,但是這個分支只有開啟的時候才能跑到,本質上還是一個分支。如果特性開關比較多,它在一定程度上會把程式碼變得很脆弱,維護起來比較麻煩。

主幹開發當很多人同時參與時,程式碼衝突的機率很大,而且特性開發的時候也有很多的風險,大家彼此之間需要隔離。

Git-Flow

Git—Flow的基本原則是需要什麼分支就給什麼分支,任何事都有很明確的分支。比如說要整合,就有develop分支,要開發就有feature分支,要釋出有release分支,每個都是不同的分支。每種型別的分支都有確定的用途。

比如說feature分支,是很多個feature並行開發的時候用來去做工作隔離,避免彼此之間有衝突。而release分支是用來做釋出的隔離,使得釋出之間不會有衝突。

我們發現這種模式很好地做了隔離,但是在資訊同步的過程中,它需要基於develop頻繁地整合去做同步,並且在各個分支中間做相應的cherry-pick或者是rebase這樣的方式來做的。

這個時候,我們就會發現分支太多,而且一個commit從feature開發到最終釋出要經歷好幾個分支,其中分支的流轉和merge規則非常麻煩。

所以Git—Flow也不是仙丹,過多的分支增加了分支管理的複雜度。還有如果Feature分支的生命週期特別長,它的合併耗時也會變得很長。而且Develop分支和Master分支同時存在,好像Develop分支的意義不是特別大。另外區分Feature分支和hotfix好像意義也不是特別大。

所以Git—Flow雖然增加了很多的分支,讓各種工作儘可能地隔離開來,但是它資訊同步是很麻煩的,而且它管理這些分支的難度也特別大。

GitHub-Flow

GitHub引入了一個分支模式叫GitHub—Flow,明顯比Git—Flow簡單很多。沒有Develop,沒有hotfix,也沒有Release,當需要開發的時候拉一個Feature分支,開發完就合併Master做釋出。

這個過程中,它的隔離只發生在開發過程中,它的資訊同步通過持續地往Master去做整合,和頻繁從Master裡面Pull程式碼來實現。它的釋出過程是基於主幹Master分支做的,因此沒有在釋出的過程中做相應地隔離。

這時候又會帶來一個問題,就是Master分支需要做持續整合,這個分支既是整合的地方也是釋出的地方。一旦整合後出現問題,它會把所有的工作堵塞掉,無法釋出也無法合併。

所以GitHub—Flow很簡單,可以做相應地隔離,但是如果說本身基礎設施或工程能力比較弱,它會限制你整合和釋出的頻率。

GitLab-Flow

GitLab—Flow和GitHub—Flow區別是在釋出過程中有了Pre-production分支和Production分支,基於開發、整合和釋出過程中不同的環境分配了相應的分支。

完成整合以後是在Master分支上,下面一步將會切換到預發分支上。對應Commit的版本已經達到了預發的條件,在預發上做完驗證以後再將其同步到Production分支,說明它已經達到了釋出的條件,所以它是逐級Promotion(晉級)的過程。逐步從整合的環境Promotion到預發環境,再Promotion到生產環境。

我們簡單地介紹了一些常見的分支模式,下面我們再來比較一下他們之間的優劣。

常見分支模式優劣對比

TBD分支少,實施簡單,做起來不需要太多的理解成本。但是它對團隊協作的成熟度和紀律都有很高的要求,一旦有人不遵守紀律,那主幹就會成為你的夢魘,這時就很難很好地去做持續地整合和釋出了。一旦它出現問題,所有人都被Block,這是主幹方式的優缺點。

Git—Flow特性之間可以並行開發,規則很完善,每個分支的職責特別明確,再大的團隊協作基本上也不會有太多的問題,但是它分支太多,規則太複雜,而且分支生命週期長,合併衝突會比較頻繁。尤其是Develop,Master是長期存在的。

對於GitHub—Flow,Git—Flow能支援的基本上它也能支援,但是這裡面有一個問題,它的整合只有在Master分支去做,因此對整合紀律有很高的要求,而且整合和釋出在一個分支上,一旦整合分支中斷,無論是整合還是釋出都會被中斷。

Gitlab—Flow也是並行開發,但是開發分支還是會有生命週期長的問題,有合併衝突的風險。另外,釋出分支之間是有耦合的,比如說Prodution和Pre—Prodution之間,是基於Promotion來耦合,所以彼此之間也是一種中斷阻塞的方式,而且很多的開發分支,Prodution和Pre—Prodution,也增加了分支管理的複雜性。

因此,我們發現沒有哪個分支模式是絕對好的,也沒有哪個是絕對差的。

對於分支有一個簡單的原則,即控制分支數目,小批量頻繁整合。控制分支的數目也就是做到工作隔離,但是又增加太多管理成本。而小批量頻繁整合可以加速資訊同步。

所以一個簡單的原則就是,從最大化生產力和最小化風險的角度,儘可能地控制分支的數目和小批量頻繁整合。

最大化生產力:所有人工作在公共區域內。除了一條長期的,不被中斷的開發主幹外,沒有任何分支。也並無其他規則,程式碼的提交過程相當簡單。但是,每一次的程式碼提交,都有可能破壞整個專案的整合,進而導致專案進度的中斷。

最小化風險:所有人都工作自己的分支上。每個人的工作是相互獨立的,沒人可以打斷其他人的工作,這樣,減少了開發被打斷的風險。但是,這種做法卻增加了額外的流程負擔,同時,協作變得非常困難,所有人都不得不謹小慎微地合併自己的程式碼,即便是整個系統中非常小的一部分,也是如此。

那麼怎麼設計或選擇適合自己的分支模式?下一篇文章,我們將繼續分享,不同的團隊如何選擇適合自己的研發模式。

原文連結

本文為阿里雲原創內容,未經允許不得轉載。