Code Review#
本章由 Tom Manshreck 與 Caitlin Sadowski 撰寫,探討 Google 如何將 Code Review 打造成一項可規模化的核心流程。Code Review 不只是找 Bug 的手段,更是促進知識共享、維護程式碼一致性、以及建立團隊文化的關鍵機制。
Code Review 流程#
Code Review 是指由作者以外的人審查程式碼的過程,通常發生在程式碼被提交至 codebase 之前。業界的實施方式各有不同:有些組織設有跨 codebase 的「把關人」(gatekeeper)小組來審查變更,有些則將審查權委派給較小的團隊,允許不同團隊要求不同層級的審查。在 Google,幾乎每一筆變更都必須在提交前經過審查,每位工程師都有責任發起和參與審查。
Google 使用自研的 Code Review 工具 Critique 來支援這個流程(另也使用 Gerrit 審查 Git 程式碼,主要用於開源專案)。本章聚焦於流程本身而非特定工具,因為這些核心原則比工具本身更為久遠,且可適用於任何 Code Review 工具。
Code Review 的某些好處(例如在程式碼進入 codebase 前偵測到 Bug)已廣為人知。然而,其他效益則更為微妙,包括心理層面的影響,這些隨著時間與規模成長會為組織帶來顯著價值。
典型的 Code Review 步驟#
在 Google,Code Review 發生在變更提交至 codebase 之前,亦稱為 precommit review。主要目標是讓另一位工程師同意這筆變更,以 “looks good to me”(LGTM)標記。LGTM 是一個必要的權限「位元」(bit),與其他核准位元結合後,才允許變更被提交。
一次典型的 Code Review 會經歷以下步驟:
- 作者建立變更:在自己的工作區撰寫程式碼,建立一個 snapshot(包含 patch 與對應描述),上傳至 Code Review 工具,產生與 codebase 的 diff 以供評估
- 自我審查與送出:作者可先利用自動化審查工具或自行檢視。滿意後,將變更寄送(mail)給一位或多位 Reviewer,通知他們查看並評論這個 snapshot
- Reviewer 提出意見:Reviewer 在 Code Review 工具中打開變更,並在 diff 上留下評論。有些評論要求明確解決(explicit resolution),有些僅為參考資訊
- 作者回應並修改:作者根據回饋修改程式碼、上傳新的 snapshot,並回覆 Reviewer。步驟 3 與 4 可能反覆多次
- 獲得 LGTM:Reviewer 對變更的最終狀態滿意後,標記為 LGTM。預設只需一位 Reviewer 的 LGTM,但慣例上可能要求所有 Reviewer 同意
- 提交變更:獲得 LGTM 後,作者在解決所有評論且變更已通過核准的前提下,將變更提交至 codebase
程式碼本身就是一種負債(liability)。 它或許是必要的負債,但就其本身而言,程式碼只是給未來某人的維護任務。新功能固然必要,但在動手寫程式碼之前,應確認需求是否真正存在、是否已有類似的實作。重複的程式碼不僅浪費精力,還會增加日後維護成本——在單一模式下很容易完成的變更,一旦存在重複程式碼就需要更多工夫。Google 內部甚至有一句話:「如果你從零開始寫,那你就做錯了!」Code Review 不應成為重新辯論先前設計決策的場合——設計討論應在更早的階段透過設計提案、API 審查等方式完成。
Google 的 Code Review 機制#
前面概述了 Code Review 流程的大致運作方式,但魔鬼藏在細節中。本節將詳細介紹 Google 的 Code Review 如何運作,以及這些實踐如何讓它隨時間適當地規模化。
在 Google,任何變更要被提交,都需要取得三種面向的「核准」:
- 正確性與理解度(LGTM):由另一位工程師確認程式碼是否正確且符合作者的描述。這通常由團隊成員擔任(但不限於此),反映在 LGTM 權限位元上——當同儕 Reviewer 認為程式碼「looks good」時設定此位元
- Code Owner 核准:由該目錄的 Owner 確認變更是否適合納入其負責的 codebase 部分(可被 check in 到特定目錄)。若作者本身就是 Owner,此核准可隱含完成。Google 的 codebase 採用樹狀結構,不同目錄有其階層式的 Owner
- Readability 核准:由具備該程式語言 Readability 認證的工程師確認程式碼是否符合語言風格與最佳實踐。這些工程師來自全公司的 Readability 認證工程師池。若作者已具備 Readability,此核准亦可隱含完成
雖然這三層控制聽起來可能繁瑣——確實有時如此——但在大多數情況下,一位 Reviewer 可以同時扮演三種角色,大幅加速流程。重要的是,作者也可以承擔後兩種角色:一位同時具備 Owner 權限與 Readability 認證的工程師,只需另一位工程師的 LGTM 即可將程式碼提交到自己負責的 codebase。
這三個權限位元可以任意組合。一位身為 Owner 且具有 Readability 的 Tech Lead 只需一個同儕 LGTM 即可提交變更;一位沒有這些權限的實習生也可以提交相同的變更到相同的 codebase,只要取得具備 Owner 權限和 Readability 的工程師核准。作者甚至可以明確要求所有 Reviewer 都給予 LGTM。
兩階段審查#
在需要多重核准的情況下,通常採取兩階段流程:
- 先取得同儕工程師的 LGTM——主要 Reviewer 聚焦於程式碼正確性與變更的整體有效性
- 再取得 Code Owner / Readability Reviewer 的核准——聚焦於這筆變更是否適合其負責的 codebase 部分、是否易於維護
這種分工讓不同角色可以專注於不同面向,節省審查時間。Code Owner 作為 Approver 時,往往關注的問題與 Peer Reviewer 不同:「這段程式碼容易還是難以維護?」「它是否增加了我的技術債?」「我們的團隊是否有專業能力維護它?」
若三種審查都能由一位 Reviewer 處理,為何不總是讓這樣的人處理所有 Review?簡短的答案是規模化(scale)。將三種角色分開,為 Code Review 流程增加了彈性。例如,你可以先讓團隊成員審查程式碼正確性,經過數輪來回(可能跨越數天)取得 LGTM 後,再只需找到程式庫的 Owner(通常也具有 Readability)核准即可。
Ownership 機制#
在小型團隊中,授予所有人存取整個 repository 的權限是常見做法。但當團隊擴大時,這種方式無法規模化。Google 的解決方案是 Ownership 機制。
Google 的 codebase 採用階層式目錄結構,每個目錄可包含特殊命名的 OWNERS 檔案,列出對該目錄及其子目錄具有擁有權責的人員使用者名稱。Ownership 並非「擁有」程式碼的概念,而是一種管理職責(stewardship)——以公司最佳利益來管理 codebase 的某個部分。(作者坦言,如果能重新命名,「stewards」會是更好的用詞。)
OWNERS 機制的運作方式:
- OWNERS 檔案可包含對其他 OWNERS 檔案或外部存取控制清單的引用,但最終都會解析為個別人員名單
- 每個子目錄可有獨立的 OWNERS 檔案,關係是階層累加的:一個檔案的 Owner 通常是其上層目錄所有 OWNERS 檔案成員的聯集
- 根目錄的 OWNERS 可作為大規模變更(Large-Scale Changes)的全域核准者,不必打擾各地方團隊
- OWNERS 檔案也充當一種文件,讓人和工具都能藉由往上走訪目錄樹來找到負責人
- 新專案建立時,不需要向中央權威機構註冊——一個新的 OWNERS 檔案就足夠了
- 團隊被鼓勵維持精簡且明確的 Owner 名單,不應將 Ownership 當作入門儀式,離開的成員應盡快移交
這個簡單但強大的 Ownership 機制在過去二十年間已證明其良好的擴展性,是 Google 確保數萬名工程師能在單一 repository 中高效運作於數十億行程式碼的關鍵方式之一。
Code Review 的效益#
在業界,Code Review 本身並不具爭議性,但也遠非普遍實踐。Google 的 Code Review 流程通常比多數公司更為徹底和廣泛。
Google 的文化,如同許多軟體公司,給予工程師在工作方式上的廣泛自由度。業界普遍認為嚴格的流程不適合需要快速回應新技術的動態公司,官僚式的規則也不適合有創造力的專業人士。然而 Code Review 是一項強制要求——少數全體軟體工程師都必須參與的通用流程之一。Google 要求幾乎每一筆程式碼變更都需經過 Code Review,不論多小。
這確實有成本,也確實會影響工程速度——它會減緩新程式碼引入 codebase 的速度,也可能影響任何特定變更的上線時間。(這兩點是軟體工程師對嚴格 Code Review 流程最常見的抱怨。)那為什麼 Google 仍然要求這個流程?為什麼 Google 認為這是長期有益的?
一個良好設計的 Code Review 流程能帶來以下好處:
- 檢查程式碼正確性
- 確保程式碼對其他工程師而言可理解
- 強化 codebase 的一致性
- 從心理層面促進團隊集體擁有感
- 促進知識共享
- 提供歷史紀錄
這些效益不僅對作者有利,對 Reviewer 同樣有益。
程式碼正確性(Code Correctness)#
Code Review 讓另一雙眼睛檢視變更是否正確運作——是否有適當的測試、設計是否合理、功能是否正確且高效。在許多情況下,正確性檢查就是確認特定變更是否可能將 Bug 引入 codebase。
IBM 的研究發現,越早在流程中發現缺陷,後續修復所需的時間就越少。投入 Code Review 的時間可以節省本來要花在測試、除錯和回歸測試上的時間——前提是 Code Review 流程本身保持輕量化。這點至關重要:過於沉重或無法適當規模化的 Code Review 流程最終會變得不可持續。
為避免正確性評估變得主觀,作者通常在設計或功能的具體方法上享有尊重優先權(deference)。Reviewer 不應僅基於個人偏好就提出替代方案,只有在替代方案能改善可理解性(例如降低複雜度)或功能性(例如提升效率)時才應提出。鼓勵工程師核准「改善 codebase」的變更,而非等待「完美」的解決方案,這有助於加速 Code Review。
隨著靜態分析和自動化測試等工具的進步,許多正確性檢查已可自動完成。然而,正確性檢查仍是整體「左移」(shift left)策略的一環,也是縱深防禦(defense-in-depth)策略中不可或缺的一環。Code Review 不需要「完美」就能產生效果。
令人驚訝的是,檢查程式碼正確性並非 Google 從 Code Review 中獲得的主要效益。更重要的是確保程式碼變更是可理解的,且隨著時間推移和 codebase 規模增長仍然合理。
程式碼可理解性(Comprehension of Code)#
Code Review 通常是作者以外的人首次檢視變更的機會。這個視角讓 Reviewer 能做到即使最優秀的工程師也無法做到的事:提供不受作者視角偏見影響的回饋。Code Review 往往是測試一筆變更是否能被更廣泛受眾理解的第一次考驗。這個視角至關重要,因為程式碼被閱讀的次數遠多於被撰寫的次數。
尋找一位與作者有不同視角的 Reviewer 往往是有用的,特別是那些可能需要在工作中維護或使用該變更中所提議程式碼的 Reviewer。與設計決策上應尊重作者不同,對於程式碼理解度的問題,建議採用 「顧客永遠是對的」 原則——應將每個疑問視為有效的。Reviewer 現在提出的任何疑問,未來都會被更多人反覆提出。這不意味著你需要改變做法或邏輯,但可能需要更清楚地解釋。
正確性加上可理解性,構成了同儕工程師給予 LGTM 的主要判斷依據。當一位工程師將 Code Review 標記為 LGTM 時,他們是在表示:這段程式碼做了它所宣稱的事,並且是可理解的。但 Google 還要求程式碼必須具有可持續維護性,因此在某些情況下需要額外的核准。
程式碼一致性(Code Consistency)#
在規模化的環境中,你寫的程式碼最終會被他人依賴,也由他人維護。許多人需要閱讀並理解你的程式碼;其他人(包括自動化工具)可能在你離開專案很久後還需要重構你的程式碼。因此,程式碼需要符合某些一致性標準,以便被理解和維護。程式碼也應避免過度複雜——簡單的程式碼更容易被他人理解和維護。
這就是為什麼 LGTM(表示正確性與可理解性)與 Readability 核准是分開的。Google 透過 Readability 制度來確保一致性:
- Readability 核准只能由通過特定程式語言 Readability 訓練的工程師授予(例如,Java 程式碼需要具備「Java readability」的工程師核准)
- 這些 Reviewer 負責確保程式碼遵循該語言的最佳實踐、與 Google codebase 風格一致、且避免過度複雜
- 如果某個模式在 codebase 中總是以同一方式實作,就更容易撰寫工具來重構它
- 一致性有時會與功能性產生衝突——Readability Reviewer 可能偏好較不複雜、功能上不一定「更好」但更易理解的方案
- 更一致的 codebase 也讓工程師更容易跨團隊進行 Code Review——工程師偶爾需要在團隊外尋求 Code Review 的幫助,能夠向專家求助並確信程式碼風格一致,讓這些專家能更專注於正確性和可理解性
心理與文化效益(Psychological and Cultural Benefits)#
Code Review 帶來重要但容易被忽略的心理與文化效益:
集體擁有感: Code Review 強化了「程式碼不是『你的』,而是團隊集體事業的一部分」這個觀念。沒有 Code Review,大多數工程師會自然傾向個人風格和自己的軟體設計方法。Code Review 流程迫使作者不僅要接受他人的意見,還要為了更大的利益做出妥協。
緩衝批評: 人類天性會對自己的作品感到驕傲,不太願意開放自己的程式碼接受他人批評。Code Review 流程提供了一個機制來緩和可能充滿情緒的互動。Code Review 在其最佳狀態下,不僅挑戰工程師的假設,而且以一種規範、中性的方式進行,緩和了批評的衝擊。畢竟,流程要求批判性審查(Google 的 Code Review 工具甚至就叫做「Critique」),所以你不能責怪 Reviewer 做了他們的工作。流程本身充當「壞警察」角色,Reviewer 則可以繼續扮演「好警察」。
漸進適應: 當然,並非所有或甚至大多數工程師都需要這樣的心理機制。但透過 Code Review 流程來緩衝批評,往往能為大多數工程師提供一個更溫和的方式來適應團隊的期望。許多剛加入 Google 或新團隊的工程師會對 Code Review 感到畏懼,容易認為批判性審查反映個人工作表現。但隨著時間推移,幾乎所有工程師都會開始期待在送出 Code Review 時被挑戰,並珍視審查過程中的建議與問題(不過,這有時需要一段時間)。
自我驗證: 即便是最優秀的工程師也可能有冒名頂替症候群(imposter syndrome)而過度自我批評。Code Review 流程作為對個人工作的肯定與認可。審查過程往往涉及想法交流和知識共享,對 Reviewer 和被審查者都有益。隨著工程師在領域知識上成長,有時很難獲得正面回饋,Code Review 流程可以提供這個管道。
促進自律: 發起 Code Review 的流程也迫使所有作者對自己的變更多花一點心思。許多軟體工程師並非完美主義者;多數人會承認「能完成工作的程式碼」比「完美但開發太久的程式碼」要好。沒有 Code Review,我們自然會走捷徑,即使本意是之後再修正。「當然,我還沒寫完所有單元測試,但我之後可以補。」Code Review 迫使工程師在送出前先解決這些問題。在送出變更前的那個反思時刻(“the little moment of reflection”)——是仔細閱讀變更、確保沒有遺漏的最佳時機。
知識共享(Knowledge Sharing)#
Code Review 是最重要但被低估的知識共享管道之一。
Reviewer 傳授知識: 大多數作者會選擇領域專家或至少熟悉該領域的人作為 Reviewer。審查過程允許 Reviewer 向作者傳授領域知識,提供建議、新技巧或參考資訊。Reviewer 甚至可以標記 “FYI” 的純資訊性評論(不需要作者採取行動,純粹作為對作者的輔助)。在此過程中對某個 codebase 領域變得特別熟練的作者,往往也會成為 Owner,進而擔任其他工程師的 Reviewer。
雙向學習: Code Review 中的回饋與確認過程涉及詢問變更為何以特定方式完成,這種資訊交流促進了知識共享。事實上,許多 Code Review 涉及雙向的資訊交流:作者和 Reviewer 都能從中學到新的技巧和模式。在 Google,Reviewer 甚至可以在 Code Review 工具中直接與作者分享建議的程式碼編輯。
跨時區與跨專案: 工程師不一定會讀每封 email,但通常會回應每一次 Code Review。Code Review 能借助 Google 的規模,跨越時區和專案,快速將資訊傳播給 codebase 各角落的工程師。Code Review 是知識傳遞的完美時機:它既及時又具可操作性。考慮到 Google 工程師在 Code Review 上花費的時間,累積的知識相當可觀——雖然 Google 工程師的主要任務仍然是程式設計,但很大一部分時間仍花在 Code Review 上。Code Review 流程提供了軟體工程師相互互動和交流編碼技術資訊的主要方式之一。
歷史紀錄: 此外,因為每筆變更都成為 codebase 的一部分,Code Review 也充當歷史紀錄。任何工程師都可以檢查 Google codebase 並確認某個特定模式是何時引入的,並調出實際的 Code Review。這種「程式碼考古」(archeology)往往能為原始作者和 Reviewer 之外的更多工程師提供洞見。
許多 Google 工程師最初「認識」其他工程師,是透過 Code Review 而非面對面互動。新的模式有時也會在 Code Review 的脈絡中被推廣,例如透過大規模變更(LSC)這類重構。
Code Review 最佳實踐#
Code Review 確實會為組織帶來摩擦和延遲。但大多數問題並非 Code Review 本身的問題,而是其特定實施方式的問題。在 Google 維持 Code Review 流程順暢運作也不例外,需要一系列最佳實踐來確保 Code Review 值得投入。這些實踐大多強調保持流程的敏捷和快速,以便 Code Review 能夠適當地規模化。
保持禮貌與專業(Be Polite and Professional)#
如同本書文化篇所指出的,Google 大力培育信任與尊重的文化,這也滲透到 Code Review 的視角中。一位軟體工程師只需要另一位工程師的 LGTM 就能滿足程式碼理解度的要求。許多工程師會在留下評論的同時給予 LGTM,表示在這些修改完成後即可提交,不需要額外一輪審查。
然而,Code Review 可能為即使是最有能力的工程師帶來焦慮和壓力。因此,所有回饋和批評必須嚴格維持在專業範疇內。
對 Reviewer 的要求:
- 應尊重作者的實作方式,只在方法有明確缺陷時才建議替代方案
- 若作者能證明多種方法同樣有效,應接受作者的偏好
- 即便發現缺陷,也應將審查視為雙方的學習機會
- 在問出「為什麼這樣做」之前,不要假設某種方法是錯的
- 在 24 個工作小時內提供回饋;若無法及時完成,至少回覆已收到並會盡快處理
- 避免零碎地(piecemeal)提供回饋——一次性提出所有意見。沒有什麼比回應了回饋後又收到不相關的新回饋更令作者沮喪
對作者的要求:
- 記住「你不等於你的程式碼」(you are not your code),check in 後程式碼就不再屬於你
- 對提問保持開放態度,準備好解釋為何以特定方式實作
- 將每條 Reviewer 評論視為 TODO 項目——不一定要照做,但必須回應
- 在雙方都有機會提出替代方案前,不要將評論標記為已解決
- 若意見不一致,可提出替代方案並請 Reviewer PTAL(please take another look)
- 身為 Code Owner 回應外部作者的變更時,只要變更改善了 codebase,仍應尊重外部作者
撰寫小型變更(Write Small Changes)#
小型變更是維持 Code Review 流程敏捷的最重要實踐:
- Code Review 應易於消化,聚焦於單一議題
- Google 的流程不鼓勵由完整功能組成的大規模變更,Reviewer 有權以「範圍過大」為由拒絕
- 「小型」通常指約 200 行程式碼以內
- Google 約 35% 的變更只涉及單一檔案
- 大多數變更預期在約一天內收到初始回饋(不一定在一天內完成審查,但會在一天內提供初步回饋)
- 幾乎所有 Code Review 都只由一位 Reviewer 審查——小型變更使這種優化成為可能
- 變更通常會抄送(CC)相關團隊,其他人可以發表評論,但主要 Reviewer 是唯一需要給 LGTM 的人
- 小型且原子性的變更更容易追蹤 Bug 來源,也更容易在必要時 Rollback
- 小型變更也讓 Approval Reviewer(Code Owner)更快速地核准,只需確認主要 Reviewer 盡了審查義務
大規模的完整功能變更容易被 Reviewer 拒絕。若需引入重大新功能,應拆分為一系列小型、漸進式的變更。雖然個別來看可能較難理解全貌,但這是一種值得的優化取捨。管理此類變更的技術包括在整合分支上開發、使用與 HEAD 不同的 diff base 等,但這些技術不可避免地帶來額外開銷。
撰寫良好的變更描述(Write Good Change Descriptions)#
變更描述是這筆變更的歷史紀錄:
- 第一行至關重要:應概述變更類型作為摘要,因為它會被用作 Code Review 工具的摘要、相關 email 的主旨行、以及 Code Search 中工程師看到的歷史摘要可見行
- 描述應詳細說明改了什麼以及為什麼改。「Bug fix」這樣的描述對 Reviewer 和未來的程式碼考古者毫無幫助
- 若包含多項相關修改,以列表形式列舉(同時保持訊息精簡)
- 描述不是為變更添加文件的唯一機會。撰寫 public API 時通常不想洩漏實作細節,但在實際實作中應大方加入註解
- 若 Reviewer 不理解某段程式碼的原因,即便邏輯正確,這也是一個好的指標——表明該程式碼需要更好的結構或更好的註解(或兩者兼具)
- 若在 Code Review 過程中做出新決策,應更新變更描述或在實作中加入適當註解
- 記住:Code Review 不只是你在當下做的事情,它也是你為後世所做的紀錄
控制 Reviewer 人數(Keep Reviewers to a Minimum)#
- Google 大多數 Code Review 恰好由一位 Reviewer 審查
- 由於一個人可以同時處理正確性、Owner 核准和 Readability 三個面向,Code Review 流程得以在 Google 規模的組織中良好擴展
- 業界和個人都有傾向尋求更多意見和一致同意,但 Google 發現這會導致邊際效益遞減——最重要的 LGTM 是第一個,後續的 LGTM 增加的價值不如想像中多
- 額外 Reviewer 的成本很快就會超過其價值
- Code Review 流程是圍繞著我們對工程師「做正確的事」的信任來優化的
- 在某些情況下,讓特定變更由多人審查可能是有用的,但即使在這些情況下,各 Reviewer 也應聚焦於同一筆變更的不同面向
盡可能自動化(Automate Where Possible)#
Code Review 是人工流程,人工投入很重要,但如果流程中有可以自動化的組件,應盡量自動化。機械性人工任務的自動化機會應被積極探索;投資於適當的工具會帶來回報。
- 過去幾年在自動化方面最重要的技術改進之一是對程式碼變更的自動靜態分析(詳見第 20 章)
- Google 的 Presubmit 機制:當變更最初被送往 Reviewer 時,presubmit 流程會先行運作。在變更被送出之前,presubmit 流程可以偵測既有變更中的各種問題——格式、lint、測試等——拒絕當前變更(避免發送尷尬 email 給 Reviewer),並要求作者先修正
- 自動化讓 Reviewer 得以專注於更重要的議題,而非格式細節
- Code Review 工具也允許作者在核准後自動提交並自動同步至版本控制系統(通常用於較簡單的變更)
Code Review 的類型#
所有的 Code Review 都不一樣!不同類型的 Code Review 需要對審查流程的各個面向有不同程度的關注。Google 的程式碼變更通常分為以下幾類(有時會重疊):
- Greenfield Review 與新功能開發
- 行為變更、改進與最佳化
- Bug 修復與 Rollback
- 重構與大規模變更
Greenfield Review(全新程式碼)#
最不常見但最重要的類型——Greenfield Review 是評估程式碼能否經得起時間考驗的最關鍵時刻:隨著時間和規模改變程式碼的底層假設,它是否會更容易維護?
全新程式碼的引入不應讓人感到意外。如同本章前面提到的,程式碼是一種負債,因此全新程式碼的引入通常應解決一個實際問題,而非僅僅提供又一種替代方案。在 Google,新程式碼和新專案通常需要先經歷廣泛的設計審查(design review),獨立於 Code Review 之外。Code Review 不是辯論過去設計決策的時候,同樣地,Code Review 也不是提出新 API 設計的時候。
為確保程式碼具有可持續性,Greenfield Review 的審查重點包括:
- 確認 API 設計與已同意的設計文件一致(可能需要審查設計文件)
- 確保完整的測試覆蓋,所有 API 端點都有某種形式的單元測試,且當程式碼的假設改變時這些測試會失敗
- 確認有適當的 OWNERS 檔案(新專案的首次 Review 往往就是為新目錄建立一個 OWNERS 檔案)
- 確認有足夠的註解,並在必要時提供補充文件
- 可能需要將專案納入持續整合(CI)系統
行為變更、改進與最佳化(Behavioral Changes, Improvements, and Optimizations)#
Google 大多數變更通常屬於對 codebase 中既有程式碼修改的大類。這些新增可能包括對 API 端點的修改、既有實作的改進、或效能等其他因素的最佳化。這類變更是多數軟體工程師日常的「麵包與奶油」。
在這些情況中,適用於 Greenfield Review 的準則同樣適用:此變更是否必要?此變更是否改善了 codebase?
審查重點:
- 對 codebase 最好的修改有時其實是刪除!清除死代碼或過時的程式碼是改善整體程式碼健康的最佳方式之一
- 行為修改應附帶對應的測試修訂,並在 CI 系統中進行增量測試(augmentation),以確保這些修改不破壞既有測試的底層假設
- 最佳化應確保不影響既有測試,可能需要附上效能基準測試(performance benchmark)供 Reviewer 參考
Bug 修復與 Rollback(Bug Fixes and Rollbacks)#
- Bug 修復應專注於修復指定的 Bug,避免順便處理其他問題——這不僅會增加 Code Review 的範圍,也會讓回歸測試更困難,且他人更難 Rollback 你的變更
- 通常應附帶修訂後的測試,以捕捉最初導致 Bug 的錯誤。Bug 之所以浮現,是因為既有測試不足或程式碼的某些假設未被滿足。作為 Bug 修復的 Reviewer,若適用的話,應要求作者更新單元測試
- 在像 Google 這樣大規模的 codebase 中,某個程式碼變更有時會導致某個依賴失敗——可能是測試未能正確偵測到,或者揭露了 codebase 中未經測試的部分。在這些情況下,Google 允許受影響的下游團隊對有問題的變更進行 Rollback(本質上是將變更還原至先前已知狀態的變更)。Rollback 可在數秒內建立——因為它只是將先前的變更還原至已知狀態——但仍需要經過 Code Review
- 任何可能導致 Rollback 的變更(這包括所有變更!)都應盡量小且原子化,以便在需要時 Rollback 不會在其他依賴上引發連鎖問題。Google 觀察到開發者會在新程式碼提交後非常快速地開始依賴它,Rollback 有時會因此影響這些開發者。小型變更有助於緩解這些顧慮——既因為其原子性,也因為小型變更的 Review 往往更快完成
重構與大規模變更(Refactorings and Large-Scale Changes)#
Google 許多變更是機器自動產生的——變更的「作者」不是人,而是機器。這就是**大規模變更(Large-Scale Changes, LSC)**流程(詳見第 22 章)。即便是機器產生的變更也需要審查:
- 低風險變更:由擁有全 codebase 核准權的指定 Reviewer 審查
- 高風險或需要領域專業知識的變更:交由個別工程師作為其正常工作流的一部分來審查
自動產生變更的審查原則:
- 乍看之下,自動產生的變更應與其他 Code Review 一樣審查:Reviewer 應檢查正確性和適用性
- 然而,Google 鼓勵 Reviewer 將評論限制在與自身程式碼相關的問題,只標記對其程式碼特定的疑慮,而非針對底層工具或產生變更的 LSC 流程本身。雖然具體變更可能是機器產生的,但產生這些變更的整體流程已經過審查,個別團隊不能對該流程行使否決權,否則無法在組織層級規模化此類變更。若對底層工具或流程有疑慮,Reviewer 可向 LSC 監督小組(LSC oversight group)反映
- Google 也鼓勵自動變更的 Reviewer 避免擴大審查範圍。審查同事撰寫的新功能時,要求作者在同一筆變更中處理相關問題是合理的;但這不適用於自動產生的變更,因為操作工具的人可能同時有數百筆變更在進行中,即便只有小比例的變更收到額外評論或不相關問題,也會限制操作者有效運作工具的規模
結論#
Code Review 是 Google 最重要且最關鍵的流程之一。它是連結工程師彼此的黏合劑,Code Review 流程是幾乎所有其他流程——從測試、靜態分析到持續整合——所依附的主要開發工作流。一個 Code Review 流程必須能夠適當地規模化,因此小型變更、快速回饋與迭代等最佳實踐對於維護開發者滿意度和適當的產出速度至關重要。
TL;DRs#
- Code Review 帶來多重效益:確保正確性、可理解性、以及 codebase 的一致性
- 永遠透過他人驗證你的假設;為讀者最佳化
- 在保持專業的前提下,提供批判性回饋的機會
- Code Review 對於組織內的知識共享至關重要
- 自動化是規模化此流程的關鍵
- Code Review 本身即提供了歷史紀錄