天天看點

【譯】如何開始CI

【譯】如何開始CI

開始學習持續內建所要了解的知識:分支政策,測試自動化,工具和最佳實踐。

目标:快速且安全地傳遞工作代碼

持續內建的目的是将代碼傳遞到存儲庫的主分支:

  • 快速地:從将新代碼推送到存儲庫以及将其合并到主分支的事情,應該在幾分鐘内完成。
  • 安全地:我們怎麼知道新代碼生效呢?持續內建會設定正确的檢查項以順利地自動合并代碼。

持續內建有點關于工具以及團隊中的思維方式和文化。你希望在開發的過程中能夠保持主分支的同時快速內建新代碼。此工作主分支将在之後啟用持續傳遞或持續部署(的操作)。但是,這些不是本文的内容。讓我們先來關注下持續內建。

實作持續內建有兩大支柱。

以小塊工作

想象一下,一個五人組成的團隊緻力于一個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檢查,那麼隻需要合并它并在之後進行代碼審查。這聽起來好像是打破了既定的過程,但是請記住​

    ​“完成比完美更好”​

    ​。如果它正常工作,它在主分支中提供的價值比停滞在一旁幾天要好。

後話