開始學習持續整合所要了解的知識:分支策略,測試自動化,工具和最佳實踐。
目標:快速且安全地交付工作程式碼
持續整合的目的是將程式碼傳遞到儲存庫的主分支:
- 快速地:從將新程式碼推送到儲存庫以及將其合併到主分支的事情,應該在幾分鐘內完成。
- 安全地:我們怎麼知道新程式碼生效呢?持續整合會設定正確的檢查項以順利地自動合併程式碼。
持續整合有點關於工具以及團隊中的思維方式和文化。你希望在開發的過程中能夠保持主分支的同時快速整合新程式碼。此工作主分支將在之後啟用持續交付或持續部署(的操作)。但是,這些不是本文的內容。讓我們先來關注下持續整合。
實現持續整合有兩大支柱。
以小塊工作
想象一下,一個五人組成的團隊致力於一個SaaS產品。每個人都開發一個單獨的新功能。每個功能的工作量大約是1或2周。有兩種方式可以實現這個目標。
- 團隊可以選擇(一個)功能分支。每個開發人員都將在這個
"功能分支"
上工作。一旦每個人對自己的工作感到滿意,此分支將被被合併到主分支。 - 團隊(仍然)可以使用分支機構,但是每次推送時,將他們的工作整合到主分支。即使事情仍然在進行中!正在進行的工作對主分支的任何終端使用者或測試者來說仍然是不可見的。
你認為哪種方法效果最好?
第一種方法最終將導致**“不可預測的釋放綜合症”**。長時效的特徵分支為每個開發人員創造了一種虛假的安全感和舒適感。由於分支分離了很長一段時間,沒辦法衡量將它們合併(到主分支)的難度。其最好的情況是出現些少的程式碼衝突,在最壞的情況下,基本的設計假設將受到挑戰,事情將不得不重新進行...這是艱難的方式。
返工的工作將在時間壓力下進行,導致質量下降和技術債務積累。這是個惡性迴圈。
請參考關於為何不應該使用特性分支來處理髒細節的文章。
第二種方法是我們實現持續整合所需要的方法。每個開發人員都在自己的分支上工作。差異是:
每次推送都會將其更改合併到主分支,每個開發人員每天會將其分支與最新的主分支版本同步幾次。
通過這種方式,團隊可以更快且輕鬆地修復衝突並協調設計假想。**早期發現五個小問題比釋出日前發現1個大問題更好。**檢視下面的“功能切換”
部分,瞭解如何將“正在進行的工作”整合到主分支。
帶有自動檢查功能的安全性
之前的軟體開發工程基於構建週期,然後是測試周期。這可能仍然適用“特徵分支”方法(法一)。如果我們每天數十次整合和合並程式碼,那麼,手動測試就沒有意義了。這將花費太長的時間。我們需要自動檢查以驗證程式碼是否正常工作。我們需要一個CI工具,幫助開發人員自動推送並執行構建和測試。
測試型別和內容應該為:
- 足夠快,能在幾分鐘內向開發人員提供反饋
- 足夠徹底,能夠安全放心地將程式碼合併到主分支
不幸的是,沒有一種方式適合所有測試型別和內容。這要根據你的專案適當平衡。在CI階段,不要執行大而耗時的測試套件。雖然這些測試提供了更好的安全性,但它們的代價就是對開發人員的延遲反饋。這將導致上下文工作切換,純粹就是浪費時間。
優化開發者時間並減少上下文切換
長時間CI檢查,我的意思是超過3分鐘的(CI),消耗團隊中的每個開發人員的大量時間。
讓我們來比較下“好”和”壞“的工作流程
。“好”的工作流程:
- 你提交併推送你的程式碼
- CI構建和測試執行1到3分鐘
- 在這1到3分鐘內,你可以檢視下手頭的任務,在某些管理工具中檢視狀態,或者再次檢視程式碼
- 在3分鐘內,你獲得CI(返回的)成功狀態:你可以繼續執行下一部分任務。如果你的構建失敗:你可以立即解決問題
“壞”的工作流程:
- 你提交併推送你的程式碼
- CI構建和測試執行15分鐘
- 你在這15分鐘內做什麼?
- 你可以和團隊一起喝杯咖啡。這很公平,但是你每天有多少這些時間呢?
- 你可能會開始關注管道(工作流)中的下一個任務
- 15分鐘之後,你收到構建失敗的通知。你需要切回到上一個任務,嘗試解決問題...並再迴圈一次15分鐘...
- 那時你可能想:我是否應該再次回到下一個任務呢,還是再等15分鐘,心平氣和地去完成當前的任務...
這糟糕的工作流程不僅僅是浪費時間。對開發人員來說也是令人沮喪的。高效的開發會使得開發人員很開心的。
你需要調整工具和工作流程以保證開發人員的滿意度。
工具
分支
持續整合是指將來自不同開發人員分支的程式碼整合到配置管理系中的公共分支。有可能你正在使用git。在git中,儲存庫中的預設主分支稱為"master"
。一些團隊建立了一個名為"develop"
的分支作為(開發時)持續整合的主分支。他們使用"master"
來跟蹤交付和部署(develop分支將合併到master分支)。
你(的專案中)可能已經有了一個主分支,你的團隊將程式碼推送或合併到那裡。堅持(這樣做)下去。
每個開發人員都應該在自己的分支上工作。如果同時處理許多不同的功能內容,可以使用多個分支。雖然這可能是"不專心"
工作的標誌。只要程式碼連貫部分準備就緒,就可以推送到你的儲存庫。如果成功,CI將檢查、啟動並將程式碼合併到主分支。如果檢查失敗,您仍然在自己的分支上,可以修復需要的任何內容並再次推送。
上述過程中的關鍵語是你程式碼連貫的部分。那麼,你怎麼知道它是連貫的?簡單。
如果你能夠輕鬆地想出一個好的提交資訊,那就是連貫的。
另一方面,如果你提交的資訊需要分三次且帶有許多形容詞或副詞,那可能並不好。多次拆分你的工作內容,連貫的提交,然後推送程式碼。連貫的提交有助於程式碼的審查,且能讓倉庫的歷史記錄更容易被遵循。
不要亂推送任何東西,因為這(有可能)意味著一天的結束!
拉取請求
pull request (拉取請求)
是什麼呢?拉取請求是種概念,其要求團隊將你的分支合併到主分支。接受你的請求應該通過你的CI工具提供的狀態和潛在程式碼審查。最終由負責合併拉取請求
的人手動合併。
拉取請求誕生於開源專案中。維護者需要一種結構化的方式來評估合併之前的貢獻。拉取請求並不是Git
的一部分。他們受到任何Git提供程式的支援(GitHub, BitBucket, GitLab, ...)。
請注意,在持續整合中,拉取請求並不是必須的。而拉取請求的主要好處是支援程式碼審查過程,這過程無法通過設計自動化。
如果你正在使用拉取請求,適用(下面)相同的原則或(上面提到的)“分塊工作”和“優化開發者時間”:
- 保持每個拉取請求內容很小,並有一個明確的目的(它將使程式碼審查更容易)
- 快速完成CI檢查
自動檢查
持續過程的核心是自動檢查。它們確保在合併程式碼後,主分支程式碼能正常工作。如果它們失敗,則程式碼不會合並。至少程式碼應該編譯或轉換,或者你的技術堆疊應該做點什麼以使其為執行時做好準備。
在編譯之上,你應該執行自動化測試以確保軟體正常工作。測試覆蓋率越高,在將新程式碼合併到主分支時你就越有信心。注意了!更好的覆蓋率意味著更多測試和更長的執行時間。你需要找到正確的權衡。
當你完全沒有測試或者需要減少一些長時間執行的測試時,你要從哪裡開始呢?專注於你專案或產品的至關重要的事項。
如果你要構建一個SaaS應用,則應該檢查使用者是否可以註冊或登入,以及執行SaaS提供的最基本操縱。除非你正在開發Salesforce競爭產品,否則你應該能夠在幾分鐘內執行測試,如果不是馬上執行。如果要構建繁重的資料處理後端:使用有限的資料集來執行不同的構建塊。在持續整合中保持大型資料集的長時間執行。合併程式碼之後,可以觸發長時間執行的測試。
專業提示
功能切換
持續整合的關鍵概念是儘快將程式碼放在主分支中,甚至工作正在進行中。如果功能不完全正常,或者你不希望暴露給測試的人員或終端使用者。實現這一目標的方法就是功能切換。在啟用/禁止
切換下啟用新功能。這切換可以是編譯時布林標誌,環境變數或執行時事物。正確的方法取決於你想要 實現的目標。
功能切換的第一個主要好處是,你可以根據需求將它們投入生產並啟用/禁止
新功能。你可以使用更改的環境變數來重新啟動伺服器,或者切換開啟/關閉
一個新的UI儀表盤的佈局。通過這種方式,你可以靈活地推出功能。如果線上上中導致意外問題,請將其禁用。或允許終端使用者選擇加入或退出該功能(使用UI切換)。
功能切換的第二個主要好處是它們會強制你考慮你正在執行的操縱與現有程式碼之間的界限。這是一個好的練習,如論何時,每次新增到現有系統時,都應該從這裡開始。功能切換步驟使得該過程的這一步更加明顯。
功能切換的唯一缺點是你需要定期從環境和程式碼中清除它們。一旦功能經過實測並被使用者採用,它應該是預設(成功的)。應該清理切換的程式碼和舊版本的東西(如果有的話)。不要陷入“配置為切換”
系統的陷阱。你無法維護和測試切換的所有組合,(帶來的缺點是)你最終擁有一個脆弱的架構。
保持CI構建時間不超過3分鐘
謹記本文中的“好”和“壞”工作流程
。我們希望避免開發人員的上下文切換工作(的情況)。拿起你的手機,並開啟3分鐘的計時器。看看你等待構建完的時間有多長!3分鐘應該是個絕對最大值,你可以集中精力並安全有效地從一個任務移動到另一個任務。
對一些團隊來說,3分鐘內的構建可能看起來很瘋狂,但這絕對可以實現的。它和你組織工作的方式有關,而不是你使用的工具。優化構建的方法有:
- 使用更多構建容納能力:如果你的CI工具上沒有足夠的併發構建和構建事件排隊,開發人員就會浪費時間
- 利用快取:大多數技術堆疊需要在執行新構建時安裝和配置依賴項。當依賴項未更改,你的CI工具應該能夠快取這些步驟,以優化構建時間。
- 檢查你的測試:檢查你的測試是否經過時間優化。刪除超時和“漫長地安全”等待步驟。如果要執行繁重的測試套件,請考慮在合併到主分支之後,在執行的單獨構建中移除它們。它們不再是持續整合保護措施的一部分,但是無論如何都不應該進行繁重的測試。
- 拆分你的程式碼庫:你必須在一個儲存庫中儲存所有東西嗎?你是否必須在所有內容上構建和執行測試,即使某些小部分發生了變化?這裡可能就是突破點。
- 有條件地執行測試:僅在某些目錄發生更改時執行測試。如果你的程式碼庫組織得很好,這將是重大的成功。
強制縮短時間來限制你的CI檢查的好處在於它使你從根本上改善整個開發過程。
正如Jim Rohn所說:
“成為一個百萬富翁,不是為了百萬美元,而是為了實現這一目標會讓你很成功”
虛擬合併:不必全憑你的程式碼
大多數持續整合工具在你的分支上執行CI構建,以確保它是否可以合併。但是這不是我們感興趣的內容。如果你知道自己在做什麼,那麼你推送的程式碼已經很有可能生效了。你的CI工具應該驗證的是你的分支和主分支合併正常。
你的CI工具應該執行分支到主分支的本地合併,並針對該分支來執行構建和測試。如果主分支在此期間沒有變化,則可以自動合併你的分支。如果確實發生了更改,則應該再次執行CI檢查,直到你的程式碼可以安全合併為止。如果你的CI工具不支援此類工作流程,請換一個工具。
邪惡的任務管理
有種誤解是,能夠跟蹤Agile板或像JIRA之類的bug跟蹤器中相關的程式碼是件很酷的(事情)。這是一個很好的教科書概念,但是對開發的過程的影響肯定不值得付出努力。工作管理員提供了“功能和錯誤”
的檢視。程式碼以非常不同的方式構建和分層。嘗試協調工作管理員中的專案和一組提交是沒有意義的。如果你想知道為什麼編寫一段程式碼,你應該能夠從上下文和註釋中獲取資訊。
後緒
工具僅僅是工具而已。設定工具可能是(花費)一個小時的事情。如果你錯誤的使用工具,你將無法得到預期的效果。
謹記我們為持續整合設定的目標:
- 快速且安全的傳輸工作程式碼
- 優化開發人員的時間並減少上下文切換
真正的意義是將你的思維方式轉變為“不斷為你的專案或產品提供價值”
。
將你的軟體開發過程視為硬體生產設施。開發人員的程式碼代表可移動的部件。主要分支就是組裝產品。
更快地將不同部分整合在一起並檢查其能正常工作,你最終將獲得更好的工作產品。
一些實操例子:
- 你正在開發一項新功能,並且必須更改其他人最有可能使用的低階別元件。為該公共元件進行相關的提交併將其合併。然後繼續處理你的其它功能。其它開發人員將能夠立即根據你的更改來開展工作。
- 你正在開發一項耗時和需要編碼的大型功能?(這時)使用功能切換。不要孤立的工作,永遠都不要!
- 你正在等待程式碼稽核,但是沒人可以執行此操作。如果你的程式碼正在通過CI檢查,那麼只需要合併它並在之後進行程式碼審查。這聽起來好像是打破了既定的過程,但是請記住
“完成比完美更好”
。如果它正常工作,它在主分支中提供的價值比停滯在一旁幾天要好。