TL;DR

模型的選擇並非絕對,你也可以用其中一種模型用到底,只是在使用上就不會這麼順暢。你可以在迭代快速的 Web 服務使用略為繁瑣的 Git Flow 也可以在需要同時支援多個版本服務的專案上使用 GitHub Flow 只是你會發現你會越用越受到流程的限制,最後要不就是放棄既有流程,不然就弄出一個屬於自己的流程(但我覺得這不見得是壞事,只是需要多一點團隊教育成本),在選用分支模型前還要先看看你們服務的上版節奏以及是否需要同時支援不同版本的情況。

Why Branching Model Matter

通常會需要考量用什麼分支模型基本上都是有協作的需求,開發的人不是只有一到兩個人,才會需要有一個大家都知道的開分支後合併的方式,才不會亂成一鍋粥。也可以在 release 時清楚的知道要從那一個 commit 出一個 artifact。

分支模型不只跟開發團隊如何協作有關,也關乎到團隊是如何做發布。近年很多在討論 DevOps 相關的場合裡滿多都在討論工具要怎麼使用,也有滿多篇幅討論怎麼做 CI/CD ,甚至是部屬時要用什麼樣的策略,金絲雀部屬、藍綠部屬等。不過卻滿少連帶討論到團隊的分支模型是怎麼選擇的。因為模型的選擇也跟團隊的部屬節奏有關連,若發布新版本的節奏很快的話(一週內就一個甚至多個新的版本推上去)甚至可以不需要 hotfix branch 的存在,但若是節奏很不穩定的話或是偏長(數週甚至數月一個版本)那 hotfix 就需要特別考量在分支模型裡面。以下就以常見的分支模型來分別探討他們適合用在哪些情境。

GIT FLOW

GitFlow illustration ref: https://nvie.com/posts/a-successful-git-branching-model/

這算是很早期提出來的分支模型。分支數量算是最多的一種,所以線圖也算是相對不需要做什麼動作就顯的複雜的一種。雖然這個模型相對麻煩,操作也相對繁瑣,但是因為發展最久大家也算是相對熟悉的模式,甚至還有 CLI 工具與 GUI 工具支援這個模型的操作。

這個模式還滿適合發布頻率較為穩定的服務,也能很好的符合稽查相關的需求,另外就是有特別切出 release branch 的關係,需要同時支援多個版版時,會比較容易一些。

分支是常見的模型中最多的,因此操作步驟相對最複雜,也滿吃團隊成員對於 git 的操作(當然是有工具可以減輕負擔跟知識要求,但出問題時的修復比較麻煩)。但需要注意的是,當同時開發的 feature branch 變多的時候管理會顯的相對不容易。

GITHUB FLOW

GitHubFlow illustration ref: https://github.com/skills/release-based-workflow

這個模式基本上是以 branch 為主的極簡化版本,捨去了 release branch, hotfix branch 和只為了發布時下 tag 使用的 master branch。保留了 main branch 同時兼具開發時為基底的需求,同時也是 release 時下 tag 的目標 branch。同樣在開發功能時會切出一個 feature branch 做管理。

雖然簡化很多事情,讓整個流程簡潔易懂很多,但是這個方式對於需要同時支援多個版本的情境並不友好,比較適合用在迭代相對頻繁的 Web service。

GITLAB FLOW

GitLab Flow 的核心原則就是「上游優先原則(upstream first)」,意即長期分支都有上下游關係,所有變動都必須先被上游分支接受,才能被套用在下游分支。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
gitGraph
    commit id: "1"
    commit id: "feat: some feature" type: HIGHLIGHT
    branch pre-production
    checkout pre-production
    commit id: "2"
    branch production
    checkout production
    commit id: "3"
    checkout main
    commit id: "another feature" type: HIGHLIGHT
    checkout pre-production
    merge main
    checkout main
    commit id: "fix bugs from pre-prod"
    checkout pre-production
    merge main
    checkout production
    merge pre-production

依據 GitLab flow 的流程圖,他可以很好的支援多個環境下的佈署。這個開發方式可以很好的支援在你無法掌控上線時程的時候。

不過這個方式要注意當你要支援的版本或是環境過多時會顯的過於複雜。

TRUNK-BASED

Trunk-based illustration ref: https://trunkbaseddevelopment.com/

這個開發流程有幾個要求,會要求至少一天要把異動合入一個共享的 branch (通常用 trunk branch 稱呼),這個共享的 branch 要能夠隨時 release。之所以會有這樣的要求的核心概念就是每一個 feature branch 都要足夠小,在合入 trunk branch 之前都要跑過自動化整合流程,確保沒有問題。

另一個 Trunk-based 開發流程比較特別的是有新的功能要釋出時會從 trunk branch 開 release branch 出來,而當 release branch 有東西要修的時候則不能直接在 release branch 身上 commit ,而是要回到 trunk branch 跑過一次既有流程後用 cherry-pick 的方式回到 release branch。

這個流程還隱含一個目標就是強迫讓你的 branch 存活時間不要太長,也可以盡可能避免 merge conflict。也因為 feature branch 都足夠小,時常合併回 trunk branch 所以常常會有開發不完全的功能推上 trunk 所以基本上都會搭配另一個 practice - feature flags 不過因為 feature flags 實做方式滿多變得(得益於日趨複雜的商業邏輯),在確定要用 feature flags 之前可以先看看這篇文章。


一些自己的心得

不管採行什麼樣的分支模式,通常盡可能讓 feature branch 的生命週期縮短是不錯的方向,若是 project 太大,就是拆分 phase go live 以及利用 feature flags 來避免影響到線上服務。主要是因為一個功能開發時間過長,會遇到的意外狀況就會越多,有些是功能之間的相互影響,有些是 spec 修改,或是上線時程修改,這個時候有一定頻率丟東西出來讓專案持續有產出的感覺,在談判桌上幫助專案穩定發展是很重要的(隕石流除外)。其中另一個好處就是這樣做 code review 時需要看的異動範圍理論上也不會太多,大家比較好掌握專案進度,範圍變小也比較好測試。

不管分支模型是不是自創的,重點是參與其中的開發人員都需要清楚的瞭解,不然都是白搭。再來就是開發人員之間的溝通要確保順暢,不管是有沒有推新 Code 上去、Spec or requirement 是否修改,在專案內的人員對於這個的認知最好要對齊,額外的溝通成本會比你想像中的更驚人。

另外就是若 repo 放在像是 GitHub / GitLab 那很推薦要善用這些平台提供的功能,像是 GitHub Action / GitLab Auto DevOps。這些工具能夠早期發現程式碼一些可能的弱點以及可以改善的內容,讓程式碼逐漸強健絕對是沒有錯的,尤其是當這樣的方式不會增添太多麻煩的時候。

Reference