敏捷團隊的代碼評審和分支策略
- 代碼評審的場景
我們可以在一定程度上使用代碼靜態分析保證代碼質量,但代碼靜態分析無法解決所有問題,也不能完全依賴他。因此在一些場景中我們需要團隊一起來做代碼評審。
-
每日代碼評審。我們一般在會在每天下午下班前拿出 1 個小時對當天的代碼做 review,如果一個團隊 8 個人的話,相當於浪費了一個人天。有一些項目經理特別不理解,爲什麼需要花時間做這個事情。實際上,每日代碼評審的意義非常重要。每日做代碼評審讓需要評審的代碼量被分攤了,隨着每天評審可以讓團隊編碼風格日趨統一,需要指出的錯誤也越來越少。而且每日代碼評審也是團隊做技術交流的一個契機,熟悉團隊其他人在做什麼。
-
發佈前代碼評審。發佈前代碼評審的目的是爲了避免明顯不合適的代碼進入產品環境的分支。有時候一些不明顯的錯誤測試人員往往沒有覆蓋住,而通過發佈前代碼評審能快速識別。如果團隊的版本管理策略是在 release 分支上發佈,可以通過和上一次的版本分支進行對比,也就是看和當前生產環境的代碼差異。發佈前代碼評審工作量比較大,一些創業團隊不會做,這是可以理解的,對於成熟的公司來說,如果有大量的用戶的產品則需要認真對待。
-
Hotfix 代碼評審。一個新版本發佈後,往往會有一些問題需要及時修復,我們叫這種臨時修復叫做 Hotfix。Hotfix 往往不會改動太多地方,測試人員也無力全部迴歸測試,所以 Hotfix 代碼一般需要通過 Pull Request 來完成讓有經驗的技術 leader 來把關合入的代碼是否會有明顯問題。
另外,每日代碼評審和上線前代碼評審往往讓團隊一起參加,可以使用一個大屏配合 IDE 在本地完成,Hotfix 代碼評審可以由團隊或者技術 leader 通過 Pull Request 來完成。
- 代碼評審的工具
做代碼評審可以用一些工具,可以提高效率。一般來說有如下幾類:
-
代碼版本管理工具。一般有 Git、SVN、Mercurial,不過目前已經是主流使用 Git 了。Git 的分佈式特性出色,工具鏈也完善,沒有別的限制因素,可以默認使用 Git。
-
代碼託管平臺。Gitlab、Github 以及國內的 Gitee 都是不錯的代碼託管平臺,如果在企業內部使用可以選擇自己搭建私有的 Gitlab,不過 Gitlab 比較複雜,可以使用 Gogos 通過 Docker 容器快速啓動一套 Git 代碼託管平臺。
-
代碼對比工具。代碼託管平臺一般提供了內置的代碼對比工具,不過訪問比較慢。還有專業的代碼對比工具軟件,比如 Beyond Compare。以及集成到 IDE Git 客戶端中的代碼對比工具,比如 Intellij IDEA 就完全夠用,而且在多人蔘與的代碼評審活動中效率也比較高。
-
專用的代碼評審工具。這類工具就是專門爲代碼評審設計的,比如 Gerrit。Gerrit 可以在網頁中做類似於 Gitlab、Github 的工作,不過還有一些額外的工作流管理的能力。
一般來說,對於非開源項目 Gitlab + Intellij IDEA 是一套比較好的工具鏈,配置和使用簡單維護成本也比較低。
- 代碼評審的注意事項
一般我們討論得比較多的是每日代碼評審,因爲需要全員參與,時間又比較有限,需要對主持人有較強的組織能力。爲了高效的進行代碼評審,團隊需要一些契約。
-
小步提交。團隊成員需要保持好的代碼提交習慣,小步提交代碼,每完成一次小階段的開發、重構工作都需要提交一次代碼,避免丟失更改的同時也爲了更好地評審代碼打下基礎。每一次提交都需要使用有意義、相同語言的文本描述。也需要遵守一些規則,比如使用看板卡片管理任務的團隊會按照 “#[卡號] [描述]” 的模式提交代碼。
-
描述具體。在代碼評審時候避免使用一些諸如:” 這個地方的實現不優雅 “這類似是而非的用語,應該使用更爲具體的方式。比如 “使用了太多個 if 語句,是否可以使用策略模式等一些設計模式改進設計”。同樣的的也需要避免帶有個人的習慣的意見,例如:“應該使用 switch 語句而非多個 if 語句組成等”。
-
及時修改。當天的代碼評審中提出的問題,需要代碼作者自行記錄,並儘可能在當天就完成修復和處理。一些零碎的小事放到以後做都是不現實的。
-
專注參與。如果是在線下參與,最好使用大屏或專門的會議室,避免使用工具或一邊做其他事情。需要全程專注參與。如果是遠程工作,對於視頻會議這種情況,需要打開攝像頭。主持人可以使用一些主持技巧,比如不定時對部分參與人點名,喚起大家注意力。
-
聚焦當下。 最糟糕的代碼評審就是突然被岔開話題,然後進行技術方案、業務方案的討論,從而浪費大家的時間。這種岔開是一個無底洞,就像《愛麗絲夢遊仙境》中 rabbit hole。代碼評審應該專注於當下的代碼問題,避免陷入技術和業務細節,如果遇到這種問題可以在專門的技術會議中討論。
-
時間控制。 代碼評審中最難的就是時間控制,一般一個正常的開發團隊每天每人的工作量至少需要 10 分鐘才能描述清楚。需要給每個人設定一個時間窗口,避免超時。一般來說我會根據人數設定時間,如果時間超過了,就立馬停止,第二天繼續,這樣會越來越快。
-
分組評審。如果團隊規模過大,無論如何也無法在 1 個小時內完成,就需要選擇分組。爲了讓所有的開發者都能瞭解全局,以及保持知識傳遞,可以讓分組每週、每個迭代重新排列。
-
知識整理。 重複出現的問題不應該被重複提出,對於一些常見的問題,團隊可以整理一份評審清單。評審清單可以用於新人蔘與到項目中來更快的適應團隊風格,也可以降低發現問題的成本和偶然性,同時開發者在提交代碼時候可以參考清單自己先評審一遍。
發佈前代碼評審可以不用全員參與,可以由技術 leader 挑選幾個關鍵人員參加即可,如果遇到無法理解的部分,可以通過查看提交人來進一步澄清。
Hotfix 代碼評審比較簡單,一般在代碼託管平臺中對 Pull Request 做出設定,必須多少人的的通過才允許合併,通過 Pull Request 也可以追溯 Hotfix 的變更記錄。
- Java 代碼評審清單
這裏整理了一份基本的評審清單用於 Java 開發者使用,清單中的條目已經考慮到了 checkstyle 和 FindBugs、Arch Unit 能檢測出來的問題,沒有將其加入其中。下面這些內容一般是無法被代碼靜態分析的內容,避免清單冗長。
-
有沒有 IDEA 的黃色警告。一般往往意味着代碼可以被優化或者潛在的問題。
-
數據輸入是否都做了驗證,比如類型、長度、格式、範圍。
-
提供的 API 是否做了鑑權,尤其是數據的鑑權。
-
是否硬編碼,需要使用常量和配置文件。
-
註釋和方法命名是否和代碼語義一致和容易理解。
-
是否使用足夠便宜的解決方案,比如庫函數提供了的邏輯不需要自己再寫一遍。
-
使用適當的數據結構,比如合理選擇 HashMap、ArrayList 等。
-
是否做了合理的異常處理。
-
API 設計是否符合規範和語義。
-
是否有足夠合理的測試。
-
Git 工作流
在團隊協作中,代碼評審的方式和代碼版本管理有一定關係,所以這裏說一說代碼版本管理。代碼版本管理的工作流和分支策略是一個討論比較多的話題。有很多流派,比如 Git Flow、GitHub Flow、GitLab Flow。
-
Git Flow: 項目存在兩個長期分支,一個主幹分支,一個開發分支,開發者一起工作在開發分支中,發佈時合併到主幹分支中。還有一些短期存在的分支,比如特性分支、預發分支等。
-
GitHub Flow: 項目存在一個主幹分支作爲長期分支,每個開發者在自己的開發分支中工作,然後通過 Pull Request 合併到主幹分支。
-
GitLab Flow:項目遵守上游優先原則,只存在一個主分支作爲長期分支。開發者在主幹分支開發,當測試穩定後,使用發佈分支發佈。後續的 Hotfix 都需要合併到發佈分支和主分支。
這三種分支策略我在不同的項目中都遇到過,從實用和經濟的情況來說,比較推薦 GitLab Flow,當然應該視團隊情況而定。比如團隊人數、是否需要和其他團隊聯調(這個是比較重要的因素,實用分支開發會比較麻煩)。
對於大多數使用敏捷工作方式的團隊,比較好的分支策略可以用一句話概括:主幹開發,分支發佈。這種方式尤其適合一個迭代一個版本這種開發節奏,如果使用持續發佈,沒有固定的版本週期這種方式未必合適。
這種分支策略有幾個規則需要團隊遵守。
-
團隊使用 Git rebase 命令拉取代碼,不允許使用 merge 操作拉取代碼,避免提交日誌混亂。
-
團隊在 master 開發,每個迭代結束前 2 天創建 release 分支,每一次 release 使用一個新的分支,使用語義化版本號來命名,但不使用修訂版本號,比如 v2.1 分支。創建 release 分支時可以同步創建 tag,以便早期發佈的 release 分支可以被刪除。
-
使用 release 分支可以部署到到預發環境,master 分支只能部署到開發和測試環境。
-
使用 release 分支發佈到產品環境,發佈前和上一次的 release 分支做發佈前代碼評審。
-
如果預發環境出現問題需要修復,使用 release 分支爲基線創建 Hotfix 分支,並提交 Pull Request,團隊批准後可以部署到預發環境,然後部署到產品環境。
-
部署到產品環境後需要將 release 分支的變更同步到 master,避免下次上線丟失更新。
-
Git Hooks 和分支保護
代碼靜態分析和代碼評審需要結合代碼的分支控制才能起到好的效果,如果 checkstyle 等檢查沒有通過,代碼不應被推送到服務器。
Git Hooks 是一些 shell 腳本,用於 git 提交某個生命週期中執行,可以在 .git/hooks/ 中被找到。用於控制 git 工作的流程,分爲客戶端鉤子和服務器鉤子。
客戶端鉤子:
-
pre-commit
-
prepare-commit-msg
-
commit-msg
-
post-commit
服務器端鉤子:
-
pre-receive
-
post-receive
-
update
一般我們會通過配置pre-commit
到項目中,約束團隊成員提交代碼時候進行一些檢查,例如:
-
運行單元測試
-
運行代碼檢查,例如 checkstyle
-
提交的 commit 信息檢查
如果有 git 服務器配置權限,也可以通過配置 pre-receive
在服務器端運行檢查。下面是 Java Gradle 的一個 pre-commit 腳本示例:
1#!/bin/sh
2 # From gist at https://gist.github.com/chadmaughan/5889802
3 set -x
4
5 # run the tests with the gradle wrapper
6 ./gradlew clean build
7
8 # store the last exit code in a variable
9 RESULT=$?
10
11 # return the './gradlew build' exit code
12 exit $RESULTs
13
在項目的根目錄中添加 pre-commit
文件,通過配置 gradle
腳本在項目初始化時,會自動安裝該 hook
。
1task installGitHooks(type: Copy) {
2 from new File(rootProject.rootDir, 'pre-commit')
3 into {
4 new File(rootProject.rootDir, '.git/hooks')
5 }
6 fileMode 0755
7 }
8
9 build.dependsOn installGitHooks
10
另外,在沒有 Pull Request 和代碼評審的情況下,也不應該把代碼直接推送到 Release 分支。Git 代碼託管平臺都會有分支保護功能,基於前面的分支策略,可以設定幾個簡單的規則。
-
受保護的分支均不可刪除、強制 push,避免代碼庫受損。
-
release 分支不接受直接 push,必須使用 Pull Request,並需要團隊 2 人以上的批准才能合併。
-
合併到 release 分支的臨時分支如果條件允許,可以自動刪除。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/6rPM8tqokKgjKnUxzRkDiQ