來源:搜狐
7991 年,隨著極限編程(Extreme programming)方法論的提出,持續集成(Continuous integration)也隨之成為一項標準化的敏捷實踐,被逐步應用于各類軟件的開發流程中。
9102 年的今天,持續集成的概念已經在軟件開發領域生根發芽,廣泛應用于不同平臺及設備的項目開發,極大提升了項目迭代速度,降低了維護成本。
不過,作為“敏捷”的固有屬性,持續集成也并不僅限于特定的模式,不同的項目可能遵循不同的實踐,形式多種多樣,效果可能也參差不齊。
為了解決這些問題,一些 Workflow 的通用模式被提出,而本文的主角,就是其中的天之驕子 —— GitHub flow。
GitHub flow 是什么?
GitHub flow,顧名思義,就是 GitHub 所推崇的 Workflow。(千萬不要理解成 GitHub 上才能用的 Workflow。)
其官網的描述為:
GitHub flow is a lightweight, branch-based workflow that supports teams and projects where deployments are made regularly.
從中我們可以得出的信息是 —— (這段描述完全就是廢話) GitHub flow 具有很高的通用性。
為了更便于了解 GitHub flow 的內容,我們從流程入手
其中的主要流程為:
新建分支(Create a branch);
提交修改(Add commits);
創建PR(Open a Pull Request);
代碼評審(Discuss and review your code);
部署(Deploy);
合并(Merge);
細心的同學可能很快會發現,GitHub flow 最大的亮點在于部署(Deploy)發生在 合并(Merge)之前,這就是 GitHub flow 的核心,非阻塞式集成 —— 在產生任何副作用之前得知當前修改的所有集成效果,達到真正的持續集成。
GitHub flow 有什么優勢?
GitHub flow 的核心優勢在于其流程帶來的自動化可能性,能夠做到其它流程無法實現的檢查過程,并極大簡化開發團隊的體力勞動,真正發揮自身的價值。
主要體現在以下方面:
基于修改的檢查
基于修改的檢查(Change-based checking) 是相對于全局檢查(Global checking)的概念,最典型的例子就是代碼覆蓋率。項目中一般會設立覆蓋率的最低閾(yù)值,并在流水線中進行檢查。
根據著名的覆蓋率第一定律:
隨著時間的推移,項目中的實際覆蓋率必將會無限趨向于要求的覆蓋率?!?沃茲基碩德
如果項目中配置的最低要求是 90%(暫不考慮覆蓋率類型),那么就不要指望實際覆蓋率能夠超過 95%。于是問題來了,全局覆蓋率要求會導致什么樣的嚴重后果呢?
我們考慮一個假象項目,總共有 100 行代碼,覆蓋率要求 90%,實際覆蓋率 90%。有一天,項目組成員小明發現其中有 10 行無意義的 console.log(42),決定將其刪除。
學過初等數學的我們都知道,對于 0~1 之間的分數,分子分母同時增加相同數值時,分數的值會增大;反之,分子分母同時減少相同數值*時,分數的值會減小。(這里要求結果仍然處于 0~1 之間。)
如果還不能反應過來的話,可能要考慮補充六個核桃了。刪除無用代碼的結果是,覆蓋率不再滿足要求,從而無法通過流水線。
90/100 * 100% = 90.00%
80/90 * 100% = 88.89%
之后,小明可以作出以下幾種選擇:
撤銷之前的修改,保留無用代碼;
降低全局覆蓋率要求;
從其它覆蓋率不足的地方補充代碼覆蓋;
找到之前導致覆蓋率不足的人,要求其補充代碼覆蓋。
選項 1 固然是最簡單的方案,直接當作無事發生。
選項 2 雖然也簡單,但是既然當前覆蓋率能夠降到 90%,如果降低要求以后必然還會繼續下降,同時如果被其他人發現可能遭到質疑(Challenge)。
選項 3 中一個覆蓋率不足的問題可能繼續分為兩種子類型:案例遺漏與非測試友好。前者是忽略了某種應當覆蓋的情況,而后者是代碼的設計本身導致無法合理測試。對于前者,如果缺乏上下文而直接把當前行為當作預期,可能會埋下錯誤隱患(如果未覆蓋當前行為本身是未定義行為甚至錯誤行為);對于后者需要進行額外的重構,仍然具備前述問題(在測試覆蓋不足的情況重構?),并且可能導致原問題遞歸(如果重構本身減少了代碼量)。
選項 4 原則上是最正確的方案,但實際上可行性很低。如果小明找到小紅,告訴她一年前編寫的代碼覆蓋率不足,那么得到的回應多半是:我當年都跑得好好的,你一改動就掛了,不是你的問題是什么?
歸根到底,不論考慮哪種選項,對于小明而言,學到的只有一件事:
永遠不要做會減少代碼的修改!
永遠不要做會減少代碼的修改!
永遠不要做會減少代碼的修改!
一旦開發團隊中每個人都認識到這一點之后,之后的開發過程就會向著堆垃圾的方向發展:能不動原有代碼就不動,實在不行寫個 if插進去。提取公共代碼?雙倍的覆蓋就是雙倍的快樂安全,怎么能說不要就不要?
不過,一旦我們使用合并前集成(Integration before Merge)的方式,便能夠得知每個改動中每個文件的覆蓋率情況,從而在開發過程中主動避免覆蓋率下滑,把質疑集中到問題的來源 —— 提交代碼并且覆蓋率不足的人身上。
非錯誤級反饋
非 GitHub flow* 的流水線中,永遠只存在一種反饋方式 —— 報錯。(為了保持簡潔,這里將所有不符合 Integration before Merge 的流程統稱為「非 GitHub flow」。)
這時候有人可能會說,我們可以向流水線的控制臺輸出里打印日志。不過我可以保證,沒有人會在正常構建的情況下守著看完每一條日志,一個合理設計的流水線也不應該需要主動關注這里的內容導致不必要的效率浪費。
日志的內容往往絕大部分都是非關鍵信息:
即便快速瀏覽日志,恐怕也很難發現關鍵信息。
不過,項目開發中往往存在很多非關鍵因素,平常不會太過關心,但一旦問題嚴重之后又會很麻煩。一個很好的例子就是應用體積。假如開發過程中對體積毫不關心(內網可能傳輸很快),那么等到用戶真正無法容忍加載時間而導致使用率急劇下降的時候,還得專門回過頭來做體積優化*。(如果本身就是打算靠創造額外優化工作賺錢的話,可以當我沒說。)
通過 GitHub flow,我們能夠在合并之前得到所有相關的信息,并自行判斷問題的嚴重性(其他 Reviewer 也有義務判斷)。如果本次改動并沒有添加新的依賴,但是構建后大小急劇增加,那么可能就需要檢查文件引用或者構建過程存在問題。
由于是基于集成結果的信息提示,因此還可以設置出現條件,例如某文件體積變化超過 0.5%。這樣能夠避免被固定消息所打擾,只關心必要內容。
除了自動執行的被動檢查項目外,對于需要可觀成本的檢查,往往設計成主動檢查項目。一般通過 PR 的標簽或者評論內容進行觸發,類似于:
性能測試(Performance testing) 就是一個較為典型的例子,如果小明不畏艱難險阻對實現代碼進行了深度重構,那么在合并前就必然選擇進行性能測試來避免非預期的影響。同理,如果只是添加了測試代碼,那么性能測試將完全沒有必要。
同樣的,Reviewer 也應當評估是否所需的主動檢查項目都被執行。
無限環境
多團隊協作項目中,一個常見的痛點就是需要根據自身或者外部的需求準備各種環境,然而一些環境在大部分時候都不會使用到,往往需要在不明所以的情況下突然增加或者調整環境配置。
這時就可以回歸到 GitHub flow 的重中之重 —— 合并前部署。
所謂的無限環境,就是自動將當前 PR 中的最新提交*部署到一個臨時環境中,并返回該環境的 URL 地址。(如果資源豐富的話也可能部署每一個提交以方便比對。)
為此,環境準備工作將變得非常簡單,只需要修改相應配置文件并創建 PR,即可得到一個對應的新環境。這一切甚至不需要依賴本地開發環境,而是直接在代碼平臺的在線編輯器中完成。
由于可以直接預覽當前修改,不會再出現不必要的疑 車 蟲無據的情況,Reviewer 有任何懷疑時便可以直接在預覽環境中驗證,而非憑空猜疑。Reviewer 也可以放心大膽的驗證自己的懷疑,不需要在本地開發環境耗時耗力地切換。
有條件時甚至可以為預覽模式設定特殊構建模式,例如高亮效果用于定位修改內容:
這樣可以極大提升 Review 效率,降低 Reviewer 的負擔。
自動化工作流
項目開發中往往有大量的時間被浪費在等待。等待構建、等待測試、等待 Review ……而這一切,都可以依靠 GitHub flow 來進行改善。
由于 PR 中能夠運行所有必要的檢查,所以本地開發環境中僅僅需要關注最可能受到影響的內容(例如當前文件的測試),而把其它不在固定影響范圍的檢查都轉交給 PR。由于 PR 的工作機制,即便存在沖突無法合并也不會導致 Push 失敗,并且 Push 本地代碼后便可以立刻關電腦走人,即便 PR 檢查失敗也不會有任何后果。
PR 中能夠利用 CI 環境,不受本機執行能力限制,因此可以并行檢查所有需驗證項目:
這里的檢查本質上仍然是 Code Review,只不過參與者不是自然人。
檢查期間,開發人員可以充分利用碎片時間處理其它事務,而無需關心檢查進度。如果任何項目檢查失敗,將立刻收到郵件通知。
如果所有檢查項目均已通過并且當前 PR 并非 Draft 狀態*,能夠自動通知 Reviewer 進行代碼 Review,并且在所有 Reviewer 同意后自動 Merge,在 Happy Path 下完全無需再次人工干預。
(Draft PR 是 GitHub 最近推出的功能,用于標記當前 PR 為未完成狀態。其它平臺可能將采用不同的判斷方式。)
說到 Reviewer,就不得不提 Code Owners。Code Owners 是 GitHub 的內置功能*,能夠配置每個文件/文件夾的所有者,在 PR 完成時根據修改文件的范圍自動向添加相應文件所有者為 Reviewer,只有當各個 Group 的 Reviewer 都同意時才允許合并。(一些第三方工具也提供了類似的機制,功能可能更加強大。)
Code Owners 充分保障了項目的可維護性,每個 Code Owner 同時具備以下職責:
Domain export:對相關代碼有深度的了解,知曉其歷史背景與特殊行為,能夠快速發現隱藏問題;
Coordinator:掌握每次修改的內容及原因,避免代碼/環境沖突及已有行為被破壞;
Contact:如果對相關代碼有疑問,能夠立即確定聯系對象而無需層層轉發;
Responsible person:如果相關代碼出現了什么意外,負責背鍋,避免不必要的推諉。
例如將環境配置文件分配個某個/某些項目組成員,那么他們就能夠充分知曉各個環境的使用情況,作出合理安排。
如何開始使用 GitHub flow?
使用 GitHub flow 的基本要求有:
具備一個代碼版本控制環境;
具備一個持續集成環境;
(可選)具備 CI 環境的管理員權限;
能夠創建一個有權限訪問 VCS 平臺的機器人帳號;
能夠自由使用 VCS 平臺的 WebHook API;
能夠自由使用 CI 平臺的 Trigger API;
(可選)能夠自由使用 CI 平臺的狀態查詢 API;
能夠創建一個高可用的內部服務器用于機器人帳號的運行;
能夠決定開發團隊的工作流程;
能夠投入成本改善基礎設施;
遺憾的是,我至今沒有過這種條件,如果你有能力去實踐 GitHub flow,希望能夠珍惜這次改善開發體驗的機會,讓更多人了解這種流程優化帶來的巨大效率優勢。
如果有任何具體的技術問題,也歡迎進一步的討論。
寫在最后
以我個人的體驗,GitHub flow 是(世界上唯一的真理)真正能夠拯救開發效率的敏捷實踐,將開發人員真正從體力勞動中解放出來,從而能夠專注于學習與思考。
如果你也覺得 GitHub flow 真正拯救了你的項目開發,不妨將它繼續推廣下去。
審核編輯 黃昊宇
-
GitHub
+關注
關注
3文章
471瀏覽量
16442
發布評論請先 登錄
相關推薦
評論