DMA01
團隊的 GIT 分支管理策略 (4) : 發佈的分支模式

團隊的 GIT 分支管理策略 (4) : 發佈的分支模式

fin

一個技術落差大的團隊還是需要從功能分支開始,搭配發佈分支,然後逐步縮減發佈間距提高頻率,經過這樣的過程讓團隊累積技術力,最終也許可以達到持續交付。

Patterns for Managing Source Code Branches 系列:

團隊的 GIT 分支管理策略 (1) : 基本概念
團隊的 GIT 分支管理策略 (2) : 主線整合與功能分支
團隊的 GIT 分支管理策略 (3) : 持續整合以及相關比較
團隊的 GIT 分支管理策略 (4) : 發佈的分支模式 (本篇)
團隊的 GIT 分支管理策略 (5) : 其他分支模式

 

部署的分支模式

繼『整合』後,『發佈』是另一個需要分支管理的階段。此篇我們一起探討常見的發佈模式,以及各自的運作方式、適用的情境等。

理想狀態下主線應該要是健康的,前一篇的持續整合情境下,所有人都可以輕鬆地直接從主線開始開發、測試、部署,當然你可能需要搭配一些部署流程以達到輕鬆地部署。在這情境下在主線標記 tag 就是我們紀錄發佈版本的方式。

當然,都說了是理想狀態,表示不是每個團隊都使用持續整合,因此會產生許多不同於主線直接部署的分支模式,以下我們就一個一個來看:

  • 發佈分支 Release Branch
  • 完備度分支 Maturity Branch
  • 環境分支 Environment Branch
  • 修補分支 Hotfix Branch
  • 發佈列車 Release Train
  • 持續交付 Release-Ready Mainline = Continuous Delivery

 

發佈分支 Release Branch

發佈分支僅接受能讓此次發佈更穩定的 commit (也就是不接受新功能或大規模的重構)

準備發佈時會直接從主線建立一個發佈分支,並且在分支上做相關的修補並整合回主線。同時開發者仍然可以在主線上整合新的變更,不會影響到發佈分支。當分支足夠穩定時就代表可以進行發佈。

麻煩之處在於修補時需要同時整合發佈分支以及主線,而當發佈分支與主線分離越久,這件事情就會變得越複雜。且因為兩邊同時需要整合,也造成有可能遺漏一方,因此有些人偏好在主線修補後再 cherry-pick 至發佈分支,如下圖。但這僅解決了會遺忘修補某一分支的問題,當兩條線差異越來越大時,修補仍然會變得越加複雜。

上述還只是比較單純的情境:一次只有一個發佈分支。有些產品或專案需要同時維護多個不同版本,比如 nodejs 。商用軟體也是一樣,客戶會因為許多原因(但主要是怕更新後影響到內部運作)而不升級版本,但又希望可以有些修補(尤其是安全性修補),此時就會存在著許多不同版本的發佈分支(windows?)。

這些發佈分支會造成維護成本逐漸地增加,因此最好說服客戶升級,而開發者可以做的就是讓版本與版本間的銜接較為順暢,降低升級的阻力。

發佈分支的優勢在於團隊能有一個專注發佈的分支去調整修補,而不用管主線的健康度或任何不是發佈分支的問題。且所有關於發佈的修正都在發佈分支上,要掌握狀態之類也相對容易。

但如果團隊只有一個產品線(或版本),那麼維持主線的健康度,讓主線隨時都可以發佈就長期來看還是可以節省比較多的精力。而如果有多個產品線時,發佈分支就成為了管理不同版本之間的分界。

發佈分支也適合用在當發佈流程中會有阻力(潛在斷點)時,比如說 mobile app 的上架審查、或是公司內部的發佈審查機制,我們就可以透過發佈分支來管理該版本的進度,讓其他成員繼續在主線開發不受影響。然而,就如同前一篇講到的整合阻力一樣,最好的方式還是盡量減少這些阻力,加速發佈的節奏。

發佈分支在某些情境下也有可能是環境分支,也有變形的永久性發佈分支,這兩種接下來都會介紹。

 

完備度分支 Maturity Branch

依據完備度建立分支,每個分支上的 HEAD 都是能達到該完備度的最新最後的變更

比較常見的完備度有: QA, Staging, Production ,依據角色不同大家看的不太一樣。比如說 QA 就會希望看到可以測試的部分、內部成員可能希望可以在 Staging 搶先看到準備上線的功能、而 Production 自然就是已經發佈的部分。這也就是說,當一段變更已經達到某個完備度,自然就會被納入該分支。

發佈分支與完備度分支一同使用時,就會如下圖,先建立一個發佈分支,然後等真正完備後丟到 production branch 後進行發佈。

完備度分支的一個好處是,如上圖,每次發布的變更都會被壓縮成一個 commit,可以比較輕易看出這次的大概變更,但這樣也有缺點,對於較為細緻的 commit-to-commit 間的變化無法掌握,也無法知道曾經有哪一些 commit,而這些應該被放在 commit message 內。

實務上完備度分支滿常見的,但不一定會把每次變更壓縮在一個 commit 中,較多的是直接把相關變更整合至該完備度的分支。

完備度分支讓人比較輕易地可以從 codebase 就看出每個完備度當下狀況(對照 HEAD),而自動化也可以輕易地達成,比如 production / qa 都直接從 HEAD 自動部署。不過此效果同樣也可以使用 tag 來達成,每次有相對應的 tag 就部署至相關環境(ex: prod-xxx 就到 production, qa-xxx 就到 qa)。

因此,即使完備度分支的確帶來一些方便,但沒有想像中的不可替代,更多的時候這樣的分支結構代表著部署流程可以再做進一步的調整。

 

變形版本:永久性發佈分支

發佈分支與完備度分支的混合版本。有一個長期永久性的發佈分支,每次需要發佈時就在這個分支上做發佈分枝的操作。在發佈分支上的修復一樣需要整合回主線。準備發佈時就直接在發佈分支上 tag 並且發佈,直到下一次需要發佈時就重複執行一樣的動作。

如同完備度分支,變更可以直接壓成一個 commit 放到發佈分支上,或直接合併(merge),如果是 merge 時就要確認發佈分支的 HEAD 與主線 HEAD 有對齊,才不會不小心合併到主線上還不想發佈的功能。

因為只有一個永久性發佈分支會比較好管理,所以比較適合單一版本的產品。

雖然這種方式可以讓我們很快速的找到最新的發佈,但就算是使用一般的發佈分支,我們也可以透過 tag 以及移動分支名稱(最新的發佈分支永遠叫做 release 之類)來達成一樣的效果。

 

環境分支 Environment Branch

建立不同分支並且透過 commit 來建立不同環境的部署

這是反模式不要用

不同的環境需要不同的環境設定,比如資料庫、主機名稱、第三方服務接口等。此模式透過建立不同的分支,並在各自的分支上做原始碼的修改以達到不同環境不同設定的效果。

版本變動時,重新建立環境分支並提交新的環境設定(?)

針對不同環境這件事情在概念上與完備度分支有些許相似之處,因此有時候也會看到完備度分支與環境分支同時使用的處理方式。不過雖然聽起來非常合理且簡單:環境就直接對應某個分支,但如同開頭講的,這就是一個反模式。

其中影響最大的缺點是:如果長期讓不同的環境有不同的環境分支,從程式碼的源頭就會有些相異之處,這會造成不同環境下的程式碼在根本上的相異,導致後續行為的不一致,增加維護的負擔。因此比較常見的準則是,相同的程式碼跑在不同環境,僅只有設定相異(而設定要不就是因為安全性不會進版控,要不就是由環境變數決定使用哪個設定),當我們把設定檔抽出,就能減少因為環境差異所造成的邏輯不一致。

只要記得,如果你的 commit 只能存在於某一個環境,卻需要在另一個環境做點修改,那這就代表了原始碼上的不一致,請避免這件事情。(原文講了很多關於這個反模式的可能效應以及處理方式,但個人認為目前主流已經不常看到環境分支,加上筆者主觀認為此文受眾應該都不太會做這種事情,故略過大量相關敘述,如有興趣請至原文觀看細節)

 

修補分支 Hotfix Branch

為了緊急修復某個問題所建立的分支

這裡的『某個問題』通常是很嚴重的問題,因此需要立即啟動處理的流程。由於是緊急事件,最高原則就是盡快的把問題修復好,減少其它阻力。

當修補完成並部署之後,就可以把修補分支整合回主線,當然如果此時同時已經有其他的發佈分支,則此修補也應該被整合至該分支。有時修補有可能與其他變動相衝突,此時好的測試就可以幫助及早發現及早治療。

修補分支與發佈分支並無太大不同,開啟新分支,大家在上面工作,並且把該修補的問題處理好。實際上,如果團隊使用發佈分支的話,修補的變更可以直接提交在當下的發佈分支並且發佈。

如同發佈分支一樣,記得要把修改 cherry-pick 到主線(通常不會反過來做因為時間緊急)。如果已經有持續部署的能力,修補分支就可以直接從主線最後一個 commit 開始。

就上圖來說,雖然團隊在 2.2 之後有新的變更 M4, M5 ,但一個成熟的持續部署團隊是可以直接最後的 M5 開始進行修補並且接著部署,而這也是持續部署帶來的好處。

在持續部署的情境下要注意的則是當修補尚未完成時不進行任何其他的主線整合,因為修補在此時是最高優先順序,自然應該優先進行整合,避免其他整合造成額外的時間浪費。

由於修補的緊急與嚴重性,透過修補分支來將事件獨立出來並且做頻繁的溝通以及管理進度就變得有其必要(中央流行疫情指揮中心是你?),當然如果修補很簡單我們也可以直接在主線上就 commit 來結束這一回合。

其實更有趣的是怎樣程度的嚴重性需要修補分支,怎樣可以依照正常開發流程去修復,而大多時候產品的發佈節奏以及商業影響會是決定如何處理問題的兩個要素。當產品的發佈間距越短,比如說持續部署,那麼就比較不需要做這樣子的區別,反正有修就上。

 

發佈列車 Release Train

預先排定好發佈的時間,就如同列車時刻表一樣,開發者(或團隊)自行決定功能要搭乘哪一個列車上線部署

發佈列車模式通常會有固定的發車週期,比如每兩週、每個月之類。開發者(或團隊)決定功能要放上哪一時刻的發佈,而等到列車啟動(部署上線)後,該分支就變成了發佈分支,不接受新功能只做修補。

以下圖為例,三月的發佈列車一但建立,開發者可以開始把想發佈的功能放入列車,直到某一個時間點會凍結功能開發 (feature-freeze),並且建立下一班列車(實務上也有可能更早建立)讓新功能有地方可以上車。凍結功能開發後進入發佈模式,開始讓此列車更為穩定,直到足夠穩定時即可上線。在此同時任何對三月列車的修補都會被複製至四月列車上(下圖黃圈)。

發佈列車常與功能分支一同使用,比如說:當 Scarlet 覺得自己快完成功能時,就可以開始思考自己的功能分支要整合進哪一個列車。

有些團隊有類似於功能凍結的軟性凍結 (soft-freeze) 機制,當列車軟性凍結後,僅接受確定完成的功能,不接受任何不穩定的或需要再調校修補的新功能,後者將會被直接踢下列車 (revert 或其他方式移除) 以避免延遲發車。

要注意的是雖然分支機制上同樣使用發佈列車,這裡講的發佈列車與敏捷領域的發佈列車是不一樣的。

發佈列車的核心概念為固定的發佈時程,這樣帶來的明顯效益是所有開發者都知道何時該上車及提前做相關準備,適合需要固定時程或是提前規劃固定發佈日期的團隊。此模式也適用於發佈流程較為費時者,比如說需要先內部測試檢查、或是需要拿到發佈核可等(如果可以的話,試著移除這些費時的步驟,可以讓發佈更為流暢更加頻繁)。當然有些步驟可能無法避免比如 app store 的審查,此時如果把發佈列車與發佈阻力 (app store) 的節奏同步,就可以減小發佈阻力對於開發節奏的影響。

固定時程的一個明顯缺點就是,較早上車的人無法較早部署,或是錯過列車的人需要再等一整個週期,這會影響到使用者回饋的速度,意味著減緩功能調整的速度。

發佈列車模式下,可以訓練團隊對於發佈節奏的掌握,建立穩定的發佈。若需要更高的交付效率,當運行順利後可以開始縮減班距,減短發佈間隔,直至最終轉換為持續交付模式。對於目標是持續交付但技能卻還不夠成熟的團隊,這樣子逐步進化的過程會比直接強迫大家進入持續交付模式來得順暢許多。

 

變形版本:複數發佈列車

基本型態的發佈列車預設只有一台,功能凍結後才會有下一台。而複數發佈列車則會預先建立未來的發佈分支(數量依照團隊需求而定,通常因為複雜度的關係頂多一到兩個),以下圖來說如果趕不上三月的發佈,那就直接提交功能到四月,並且整合三月的變動到四月列車上。

列車間整合的頻率可以如持續整合有變化就整合,也可以在列車發車後才整合,這就看團隊對於整合頻率的取捨是什麼。需要注意的是這樣的整合是單向整合,四月列車主動整合三月變動,而三月列車並不知道四月的變動,這會造成未來整合的問題(見系列文第二篇的整合頻率)。

 

基於主線的發佈列車

發佈列車如果把主線考慮進來會變成如下圖,每次列車都是由當下的主線分支出來並持續整合回主線:

如果開發者不想要此次列車就上線,他可以有兩種選擇 1. 等待發佈分支建立後再行整合至主線 2. 使用 關鍵介面功能開關 讓程式碼與系統一起上線,但實際上不會被觸發。但如果是 2 就需要注意主線的健康度。

 

持續交付 Release-Ready Mainline = Continuous Delivery

保持主線的健康度,讓主線可以隨時被部署

當你的主線足夠健康,就會有足夠的信心隨時直接從主線發佈新的版本,有了自動部署工具的話此步驟可以簡化至『下一個 tag』即可。這可以說是最輕鬆的發佈方式(當然背後需要的工作與技術含量可能是最高的),也目前最被推崇的。

需要注意的是,主線隨時可以發佈不代表真的需要隨時發佈。這就是持續交付以及持續部署的細微差異:持續交付確保主線隨時都可以發佈,但要不要或何時發佈可以另行決定,而持續部署則是主線隨時都會被部署,系統永遠與主線相同。持續部署可以看作是持續交付的一種特化版本。

 

小結

也因為持續交付所需要下的功伕比較深,通常能夠做到持續交付的團隊效能都會比較好,但這不代表持續交付就真的適用所有情境。這也是為什麼我們需要看過所有的常見模式後再來講持續交付:各種的模式都有其相對適應的情境。一個技術落差大的團隊還是需要從功能分支開始,搭配發佈分支,然後逐步縮減發佈間距提高頻率,經過這樣的過程讓團隊累積技術力,最終也許可以達到持續交付。

終極目標是主線會不斷演化,但系統可以持續維持穩定運作(關鍵字:測試、高頻率整合、模組化、關鍵介面、功能開關),如此可省去建立分支、整合分支甚至想說分支該叫什麼名稱的麻煩。

這種情境下系統品質已經內化在開發者的心態中,系統會不斷的往正向發展堆疊累積。這也是 Agile Fluency Model 下所追求的交付目標。

追蹤我們的粉絲團以獲得最新發文通知

 

 

原文出處:團隊的 GIT 分支管理策略 (4) : 發佈的分支模式

第一篇:團隊的 GIT 分支管理策略 (1) : 基本概念

第二篇:團隊的 GIT 分支管理策略 (2) : 主線整合與功能分支

第三篇:團隊的 GIT 分支管理策略 (3) : 持續整合以及相關比較

第五篇:團隊的 GIT 分支管理策略 (5) : 其他分支模式與總結

fin
fin

前後端技術、 UI/UX 、產品開發、資料分析

很雜,真的很雜

網頁開發雜記:https://www.facebook.com/thingsaboutwebdev/

fin的Medium:https://useme.medium.com/

Google UX Playbook — 【給新手的練習題】金融財經手機版
如果能依照本文的流程做完,基本上已經能熟悉專案中的需求與介面規劃階段。
遠端工作者的自我管理
近期因應肺炎疫情 (COVID-19),開始聽到越來越多的公司,為了避免群眾接觸增加感染的機會,嘗試將工作模式調整成遠端進行。對於工作者而言,似乎是一個不錯的消息。
UI/UX設計師的接案二三事(下)
延續上一篇所聊到關於接案前要具備的心態,這篇我們來聊一聊關於大家更在意的事情,也就是接案時實際會遇到的問題:成本、報價、合約等等事項。