分支模式是我們在進行代碼變更時的一種約定,它在版本管理工具(如Git)之上,約定我們在不同分支上的行為,達到提升開發協作效率的目的。
背景
讓我們回到軟件開發的早期,那時有很多獨行的勇士,他們一個人寫一個操作系統、一個周末搞一個編輯器。他們把代碼上傳到FTP上,或通過磁盤分發。這個時期軟件開發由一兩個人獨自完成,不需要版本管理工具,也沒有分支模式。
隨著通用軟件和行業軟件的發展,軟件的規模越來越大,有些軟件需要幾千名工程師,經過幾年的開發、測試,才能最終發布。大家比較熟悉的windows、office軟件等通信設備商開發的2G、3G基礎軟件是這方面的典型代表。隨著大型軟件開發的出現,版本管理工具隨之出現了,如CVS、Subversion和clearcase。這一階段通常是集中式的版本控制,分支比較昂貴,大家的提交頻率較低,代碼集成往往需要專門的配置管理工程師協助解決。軟件發布的周期從數周到數年不等。
之后軟件的應用場景越來越廣,尤其是互聯網應用的快速發展,要求軟件的發布周期越來越短。于是,微服務架構被提了出來,單體應用根據領域被拆分成多個服務,每個服務可以獨立開發、獨立測試、獨立部署。這樣,很多需求只需要涉及少數的幾個應用,在十幾人以內的小團隊內部就可以完成。互聯網應用爆發,加上Git這樣的分布式版本管理工具出現,多分支開發變得普遍,高效的開發者經常一天提交十幾次。軟件發布的周期縮短到數周乃至數天。
為什么要有分支模式
從前面的背景介紹,大家可能已經發現,分支模式是隨著軟件協作的需求和版本管理工具的發展而產生的,它的出現,本質上是為了解決兩個問題:1.沖突;2.返工。
通過避免沖突和減少返工,讓開發者關注在需求開發上,提升軟件的發布質量和發布效率。
當前,絕大多數軟件研發都采用Git為版本管理工具,為了簡單起見,之后我們提到分支模式,均特指基于Git的分支策略。
避免沖突
如果多個人同時在同一個應用的同一個分支上開發,那么他們兩人的工作很容易產生沖突。這里的沖突不一定是Git提交時的conflict。像測試被其他人破壞,發布需要等待其他人完成,都屬于沖突的范疇。簡言之,一個需求的開發被其他需求開發活動所干擾,就產生了沖突。在Subversion時代,有些團隊會采用定時排隊提交,驗證,回滾的策略,即到固定的時間點,按照協商的順序,每個人按順序提交自己的代碼,然后由配置管理員進行構建、測試,如果測試不通過,則提交被回滾。在這個期間,其他待提交人只能等待。于是乎,每次發布前,配置管理員都會異常辛苦。而由于提交不易,工程師傾向于一次提交更多的東西,但提交的越多,產生沖突的可能性也越大。
另外一種沖突,是合并的沖突,如主干上的某個增強補丁合并到發布過的一個分支上,由于代碼的基線不同,產生沖突,無法合并。
分支模式通過合理的開發、合并約定,來避免或減少上述沖突情況發生。
減少返工
由此可見,每一次返工的成本是很高的。
影響開發效率的另一個因素是返工。比如有些團隊采用窗口制的發布策略,每周四是發布窗口。在這之前,很多功能都被提交到待發布分支上,如果此時針對待發布分支的測試發現某個功能有嚴重的問題,需要將該功能相關的提交從發布分支中去掉,不然就會影響其他功能的發布。那首先,得有人將該功能相關的提交都識別出來,進行回滾,重新進行構建和測試;然后,該功能在問題修復后,需要再次提交,并且祈禱不再出現問題。
定義分支模式
那什么是分支模式,它又包含哪些內容呢?分支模式的本質是需求交付的流程和約束策略,它通常包括:
定義分支的類型及其標識,通常包括:
需求開發分支
集成分支
待發布分支
發布分支
熱修復分支
分支的生命周期及創建消亡規則。
提交在分支間的流轉規則和約束條件。
發布與代碼的對應關系,如Tag等方面。
如:某團隊有20名開發,為企業客戶提供SaaS服務,定期發布版本。他們的分支模式如下:
存在四種分支:feature分支、develop分支、master分支和hotfix分支。
feature-.*:需求開發分支
develop:集成分支和待發布分支
master:發布分支
hotfix-.*:熱修復分支
develop和master為長期分支,feature-.*和hotfix-.*為臨時分支,開始開發時從develop創建,完成后合并入develop,分支消亡。
master不允許直接提交,必須從develop分支合并過來。
每次發布會在master上創建Tag,發布與Tag一一對應。
怎么選擇分支模式
同時,如果有其他手段可以解決(如采用Feature Team減少跨團隊協作),就不要用分支模式來解決。
在搜索引擎里搜索“分支模式”,大家可能會看到五花八門的各類分支模式,著名的如Git Flow、GitHub Flow、GitLab Flow和TBD(Trunk Based Development)等。怎么選擇呢?
原則
建議是根據團隊和應用的具體情況,按照越簡單越好,越自動越好的原則來選擇。
方法
具體落地的時候,分別考慮團隊和應用的特點。
對于團隊,需要考慮的有:
團隊數量
團隊規模
團隊內協作需求
跨團隊協作需求
團隊技術偏好
對于應用,需要考慮的有:
構建依賴
運行依賴
發布依賴
單應用規模
總應用數
單次發布應用數
然后根據開發->集成->驗證->發布的流程:
從右往左來確定每個階段存在的沖突和協作需求。
判斷是否需要在該階段使用單獨的分支。
確定分支的進入條件和生命周期。
有些團隊在選擇分支模式時有個誤區,傾向于選擇看起來更專業的復雜分支模式。例如一個不到10人的團隊卻選擇Git Flow這樣復雜的模式。而事實上在10名研發規模的創業公司采用類似下圖的只有一個分支的非常簡單的分支模式,在保證微服務拆分和自動化測試的前提下,效果很好。
最后,一旦選擇了某個分支模式,就要保證切實執行,并定期評審,確保現有分支模式符合當前研發現狀需要。
怎么落地分支模式
我們以為什么要有分支模式一章中的模式為例,來看一下如何在云效Codeup中落地一個分支模式。
立即體驗:云效代碼管理Codeup
1、配置保護分支和合并條件
develop和master分支不允許開發者直接提交,我們需要將他們配置為保護分支。打開對應代碼庫的設置頁面,點擊分支設置,新建保護分支規則(以master分支為例)。
打開合并前代碼評審和自動化檢查選項并進行配置。
2、配置流水線模板
接下來,我們在云效流水線Flow中,為feature-.*、hotfix-.*、develop和master分支分別創建不同的流水線模板:
針對feature-.*分支的開發階段流水線。
針對hotfix-.*分支的緊急修復流水線。
針對develop分支的集成階段流水線。
針對master分支的發布階段流水線。
立即體驗:云效流水線Flow
3、應用流水線模板
接下來就是在各個應用中使用這些流水線模板了。對于不同的應用類型(如Java應用和Golang應用),建議在階段上應該是相同的,只是每個階段的具體內容有所差別(如Maven單元測試和goconvey單元測試)。
至此,我們就可以在既定的分支模式的約定下進行開發和發布了。
實踐建議
單主干:一個代碼倉庫應該保證有且僅有一個主干分支。
最少長期分支:在避免沖突的前提下,盡量減少長期分支的數量。
Promotion:代碼的提交應該是逐級合并,如feature->develop->master,避免feature->develop和feature->master同時存在。
發布不可變:發布的版本應該是不可變且可回溯的。
自動化事件觸發:分支的持續集成過程應該是自動化的,且通過提交事件或制品變更事件自動觸發。