
Code Review 方法論:怎麼 Review、怎麼被 Review
流程概覽
graph LR A["Author 開 PR"] --> B["Reviewer Review"] B --> C{"Discussion"} C -->|共識| D["Approve"] C -->|需修改| E["Request Changes"] E --> F["Author 修改"] F --> B D --> G["Merge"] style A fill:#4CAF50,color:#fff style B fill:#2196F3,color:#fff style C fill:#FF9800,color:#fff style D fill:#4CAF50,color:#fff style E fill:#F44336,color:#fff style F fill:#9C27B0,color:#fff style G fill:#009688,color:#fff
文章概覽
在本篇文章中,你會學到:
- 為什麼 Code Review 不只是「看程式碼」
- Author 怎麼開一個讓人願意 review 的 PR
- Reviewer 到底該看什麼、不該看什麼
- Review comment 怎麼寫才有建設性
- 被 review 的時候該用什麼心態面對
- Code Review 在 CI/CD pipeline 中的定位
- 團隊從零導入 Code Review 的實戰建議
開場:為什麼 Code Review 不只是「看程式碼」
先講一個真實場景:一個 junior 工程師開了人生第一個 PR,結果收到 47 條 comment,每一條都是「這裡不對」「改成這樣」,沒有一條解釋為什麼。他改了三天,信心全碎,從此對 Code Review 有 PTSD。
這不是 Code Review,這是公開處刑。
Code Review 的本質是三件事:
- 知識傳遞:Senior 的經驗透過 review 傳給 junior,junior 的新想法也能讓 senior 學到東西
- 品質把關:多一雙眼睛就少一個 production bug,這個 ROI 很劃算
- 團隊文化:好的 review 文化會讓團隊有「我們一起把 code 變好」的感覺,不是「你寫的爛我來挑毛病」
如果你的團隊做 Code Review 做到大家都在逃避、PR 放三天沒人看、或者 review 變成政治鬥爭——那不是 Code Review 有問題,是做 Code Review 的方式有問題。
Author 的責任:怎麼讓 PR 好 Review
你一定收到過那種只寫 fix bug 的 PR title 對吧?打開一看,改了 87 個檔案、2000 行 diff。你內心 OS:「我到底看了什麼?」然後默默按了 Approve。
這就是典型的「reviewer 擺爛」,但真正的問題在 author 身上——你沒有讓 PR 好 review。
PR 大小控制
| PR 大小 | 行數 | Review 品質 | 現實情況 |
|---|---|---|---|
| 理想 | < 400 行 | 高,reviewer 能仔細看 | 30 分鐘內 review 完 |
| 可接受 | 400-800 行 | 中,容易漏看細節 | 需要 1 小時以上 |
| 太大了 | > 800 行 | 低,reviewer 會開始略讀 | 大家會直接 Approve |
Google 的研究數據:PR 超過 400 行之後,reviewer 發現 bug 的能力會急邁下降。
怎麼控制 PR 大小?
- 拆 feature:一個大 feature 拆成多個 PR
- 先 refactor 再加功能:拆成兩個 PR
- 不要混改:修 bug 就修 bug,加 feature 就加 feature,不要順手改 coding style
PR Description 怎麼寫
PR description 是 reviewer 的地圖。沒有地圖,reviewer 只能在你的 code 裡迷路。
好的 PR Description 範本:
## What
修正購物車在多幣別切換時的匯率計算錯誤
## Why
用戶反映:切換幣別後,總金額顯示錯誤(Jira: CART-1234)
根因:匯率快取沒有在幣別切換時清除
## How
- 在 CurrencyContext 切換時清除匯率快取
- 加入 useEffect cleanup 確保切換時重新 fetch
- 加了 3 個 unit test 覆蓋主要場景
## Test
- [x] 單元測試通過
- [x] 手動測試 USD -> TWD -> EUR 切換
- [x] 確認 staging 環境正常
## Screenshot
(附上修復前後的截圖)不好的 PR Description:
fix cart bug就這樣。什麼都不知道。Reviewer 看到這種 PR,內心只想按 Approve 趕快結束。
Self-Review Checklist
開 PR 之前,先自己 review 一次。這不是可選的,這是必要的。
| 檢查項目 | 為什麼重要 |
|---|---|
| diff 裡有沒有 debug log / console.log | 忘記刪很丟臉 |
| 有沒有不該進 commit 的檔案 | .env、node_modules、.DS_Store |
| 變數命名有沒有一致 | 一下 userId 一下 user_id |
| 有沒有寫測試 | 至少 happy path 要有 |
| PR description 寫了嗎 | 不要只寫 fix bug |
如果你自己都不想 review 自己的 code,你怎麼能期待別人認真 review?
Reviewer 的責任:三層 Review 法
很多人 review code 就是從第一行看到最後一行,看到什麼就 comment 什麼。這樣效率很低,而且容易見樹不見林。
推薦用三層 Review 法,從大到小:
第一層:架構與設計(最重要)
問自己這些問題:
- 這個解法的方向對嗎?
- 有沒有更好的設計?
- 這個改動會不會影響到其他系統?
- 新加的 dependency 合理嗎?
範例:發現 N+1 Query 問題
// PR 裡的 code
async function getOrdersWithItems(userId: string) {
const orders = await db.orders.findMany({ where: { userId } });
// 這裡每個 order 都會發一次 query,典型的 N+1
for (const order of orders) {
order.items = await db.orderItems.findMany({
where: { orderId: order.id }
});
}
return orders;
}如果你在這裡只是 comment「變數名可以改一下」,那你就錯過了真正的問題。正確的 review comment 應該是:
[must]這裡有 N+1 query 問題。如果用戶有 100 筆訂單,這段 code 會發 101 次 query。建議改用include一次撈完:const orders = await db.orders.findMany({ where: { userId }, include: { items: true } });
第二層:邏輯正確性
- 邊界條件有沒有處理?(null、空陣列、超大數字)
- 錯誤處理完整嗎?
- 有沒有 race condition?
- 商業邏輯對嗎?
範例:發現邊界條件遺漏
function calculateDiscount(price: number, quantity: number): number {
if (quantity > 10) {
return price * quantity * 0.9;
}
return price * quantity;
}Review comment:
[must]price或quantity如果是負數或 0 呢?建議加 validation:if (price <= 0 || quantity <= 0) { throw new Error(`Price and quantity must be positive`); }
第三層:風格與細節(最不重要)
- 命名清不清楚
- 格式一不一致
- 註解有沒有過時
為什麼說這層最不重要?因為這些應該交給工具。ESLint、Prettier、EditorConfig 能處理的事,不要浪費人類的時間。
原則:能自動化的就自動化,人類只 review 機器做不到的事。
Review Comment 怎麼寫:四種前綴
Comment 沒有分類,reviewer 和 author 都會很痛苦。Reviewer 不確定哪些是必須改的、哪些只是建議;author 不知道改完哪些就能 merge。
推薦用四種前綴來分類 comment:
| 前綴 | 含義 | Author 需要的動作 |
|---|---|---|
[must] | 必須修改,不改不能 merge | 一定要改 |
[nit] | 小建議,不影響功能 | 可改可不改 |
[question] | 我看不懂,想問 | 回覆解釋就好 |
[nice] | 寫得好,給個讚 | 開心一下 |
好的 Comment 範例
[must] 安全性問題:
[must]這裡直接把 user input 拼進 SQL query,有 SQL injection 風險。建議改用 parameterized query:// 危險:直接拼 user input const result = await db.query("SELECT * FROM users WHERE name = " + name); // 安全 const result = await db.query("SELECT * FROM users WHERE name = ", [name]);
[nit] 小建議:
[nit]這個 function 叫doStuff不太清楚在做什麼,建議改成calculateShippingFee之類的。不改也行,不影響功能。
[question] 看不懂:
[question]為什麼這裡用WeakMap而不是普通的Map?是為了避免 memory leak 嗎?如果是的話可以加個註解說明。
[nice] 讚美:
[nice]這個 error boundary 的設計很優雅,fallback UI 和 error logging 都考慮到了。學到了!
壞的 Comment 範例
- 「這裡不對」——什麼不對?怎麼改?
- 「我覺得這樣比較好」——為什麼比較好?有什麼根據?
- 「我以前都不是這樣寫的」——所以呢?
- 「改成 xxx」——為什麼?不解釋原因的 comment 就是命令,不是 review
Review Comment 的黃金原則:指出問題 + 解釋原因 + 給出建議。
三個元素缺一不可。光指出問題沒有原因,author 會覺得你在找碴。光有建議沒有原因,author 不知道為什麼要改。
被 Review 的心態:你的 Code 不是你
這是 Code Review 最重要但最常被忽略的一塊。
很多工程師把 code review 當成人身攻擊。收到一條 [must] comment 就覺得「他是不是覺得我很爛?」。然後開始防禦性回覆:「這本來就可以動啊」「你不懂需求」「之前都是這樣寫的」。
核心心態:Code 是 code,你是你。Reviewer 在 review 的是程式碼,不是你這個人。
幾個具體建議:
- 不要急著回覆:看到 comment 先深呼吸,理解 reviewer 想表達什麼
- 假設善意:除非有明確證據,否則假設 reviewer 是想幫你把 code 變好
- 可以不同意:不同意就好好討論,用技術論點,不要用情緒。「我的理解是 A 比 B 好,因為 X、Y、Z」比「你錯了」有效一百倍
- 說謝謝:有人花時間認真看你的 code,這本身就值得感謝
- 分辨 must 和 nit:
[must]一定要處理,[nit]可以決定要不要改。不要每條 comment 都當成命令
一個好的 Author 回覆範例:
Reviewer:
[question]為什麼這裡用 WeakMap?Author: 好問題!因為這個 cache 的 key 是 DOM element,用 Map 的話 element 被移除後 reference 還在,會造成 memory leak。WeakMap 的 key 是 weak reference,element 被 GC 時 cache entry 也會自動清除。我加個註解說明。
看到了嗎?不防禦、不攻擊,正面回答問題,還主動加註解讓 code 更清楚。這就是 senior 的表現。
Code Review 在 CI/CD Pipeline 的定位
Code Review 不是孤立的活動,它是 CI/CD pipeline 的一環。
graph TB A["Developer Push"] --> B["CI: Lint + Format"] B --> C["CI: Unit Test"] C --> D["CI: Build"] D --> E["Human: Code Review"] E --> F["CI: Integration Test"] F --> G["Deploy to Staging"] G --> H["QA Verification"] H --> I["Deploy to Production"] style A fill:#4CAF50,color:#fff style B fill:#FF9800,color:#fff style C fill:#FF9800,color:#fff style D fill:#FF9800,color:#fff style E fill:#2196F3,color:#fff style F fill:#FF9800,color:#fff style G fill:#9C27B0,color:#fff style H fill:#009688,color:#fff style I fill:#F44336,color:#fff
重點是:人類 review 放在 CI 自動檢查之後。
為什麼?因為 lint error、format issue、test failure 這些應該在 CI 階段就被擋掉。Reviewer 不應該花時間告訴你「這裡少一個分號」或「測試沒過」——這是機器的工作。
| 自動化負責 | 人類負責 |
|---|---|
| Lint / Format | 架構設計合理性 |
| Type checking | 商業邏輯正確性 |
| Unit test / Integration test | 命名是否清楚(超越 lint 規則) |
| Security scanning | 是否有更好的設計方案 |
| Bundle size check | 知識分享與教學 |
| Coverage threshold | 跨系統影響評估 |
CI 過了才開 review,reviewer 到手的 PR 至少是「能跑的 code」。 這樣 reviewer 才能專注在真正需要人類判斷的事情上。
Branch Protection Rules
建議的 GitHub branch protection 設定:
- Require at least 1 approval before merge
- Require status checks to pass (CI green)
- Require branches to be up to date
- Dismiss stale reviews when new commits are pushed
- Do NOT require review from code owners(除非團隊夠大)
團隊導入 Code Review 的實戰建議
如果你的團隊之前沒有做 Code Review,不要一次到位。漸進式導入才不會讓大家反感。
Phase 1:建立基礎(第 1-2 週)
- 開始用 PR,不要直接 push to main
- PR 至少要有 description(What / Why / How)
- 設定 CI:lint + test 要過才能 merge
- 先不強制 review,讓大家習慣流程
Phase 2:開始 Review(第 3-4 週)
- 要求至少 1 個 approve 才能 merge
- 導入 comment 前綴系統
- 設定 review SLA:24 小時內要有第一次 review
- 鼓勵 nice comment,讓 review 不只是挑毛病
Phase 3:優化流程(第 5-8 週)
- PR size < 400 行的規範
- PR template(自動帶出 What / Why / How / Test)
- Review rotation:不要永遠都是同一個人 review
- 定期回顧:review 速度、品質、大家的感受
Phase 4:進階實踐(第 9 週以後)
- Pair programming 搭配 Code Review
- Architecture Decision Records (ADR)
- 自動化更多檢查(security scan、performance benchmark)
- Review 數據追蹤(merge time、comment 數量、iteration 次數)
常見阻力與應對
| 阻力 | 應對方式 |
|---|---|
| 「我沒時間 review」 | Review 就是工作的一部分,不是額外的事。把 review 時間排進 sprint |
| 「Review 太慢影響進度」 | 設定 SLA,24 小時內必須 review。PR 太大的話拆小 |
| 「被 review 很不舒服」 | 培訓 reviewer 怎麼寫好的 comment,先從 nice 開始 |
| 「Senior 都不認真 review」 | Lead 以身作則,review 品質也是績效考核的一部分 |
| 「PR 放很久沒人看」 | Bot 提醒、Slack 通知、standup 追蹤 |
Review SLA 建議
| PR 類型 | 首次 Review | 完成 Review |
|---|---|---|
| Hotfix | 2 小時內 | 4 小時內 |
| 一般 Feature | 24 小時內 | 48 小時內 |
| Refactoring | 48 小時內 | 一週內 |
| Documentation | 48 小時內 | 一週內 |
延伸閱讀
- Google Engineering Practices - Code Review — Google 內部 Code Review 指南,業界公認的最佳參考
- Conventional Comments — Comment 前綴系統的靈感來源
- How to Make Your Code Reviewer Fall in Love with You — 從 Author 角度寫的實戰文章
- The Standard of Code Review — Google 對 Reviewer 標準的定義
結語
Code Review 做得好,整個團隊的 code quality 和工程文化都會往上提升。做得不好,就是浪費時間還傷感情。
記住幾個核心原則:
- Author:讓你的 PR 好 review——小、清楚、有 context
- Reviewer:從架構到細節三層看,comment 要有前綴和原因
- 兩邊都是:假設善意、對事不對人、說謝謝
- 團隊:漸進式導入、自動化能自動化的、追蹤數據持續改善
最後,Code Review 的終極目標不是「抓 bug」,而是讓團隊裡每個人都能寫出更好的 code。這是一個學習的過程,不是考試。
好的 Code Review 文化,是一個團隊最有價值的無形資產之一。