cover

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 的本質是三件事:

  1. 知識傳遞:Senior 的經驗透過 review 傳給 junior,junior 的新想法也能讓 senior 學到東西
  2. 品質把關:多一雙眼睛就少一個 production bug,這個 ROI 很劃算
  3. 團隊文化:好的 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] pricequantity 如果是負數或 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 的是程式碼,不是你這個人。

幾個具體建議:

  1. 不要急著回覆:看到 comment 先深呼吸,理解 reviewer 想表達什麼
  2. 假設善意:除非有明確證據,否則假設 reviewer 是想幫你把 code 變好
  3. 可以不同意:不同意就好好討論,用技術論點,不要用情緒。「我的理解是 A 比 B 好,因為 X、Y、Z」比「你錯了」有效一百倍
  4. 說謝謝:有人花時間認真看你的 code,這本身就值得感謝
  5. 分辨 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 週)

  1. 開始用 PR,不要直接 push to main
  2. PR 至少要有 description(What / Why / How)
  3. 設定 CI:lint + test 要過才能 merge
  4. 先不強制 review,讓大家習慣流程

Phase 2:開始 Review(第 3-4 週)

  1. 要求至少 1 個 approve 才能 merge
  2. 導入 comment 前綴系統
  3. 設定 review SLA:24 小時內要有第一次 review
  4. 鼓勵 nice comment,讓 review 不只是挑毛病

Phase 3:優化流程(第 5-8 週)

  1. PR size < 400 行的規範
  2. PR template(自動帶出 What / Why / How / Test)
  3. Review rotation:不要永遠都是同一個人 review
  4. 定期回顧:review 速度、品質、大家的感受

Phase 4:進階實踐(第 9 週以後)

  1. Pair programming 搭配 Code Review
  2. Architecture Decision Records (ADR)
  3. 自動化更多檢查(security scan、performance benchmark)
  4. 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
Hotfix2 小時內4 小時內
一般 Feature24 小時內48 小時內
Refactoring48 小時內一週內
Documentation48 小時內一週內

延伸閱讀


結語

Code Review 做得好,整個團隊的 code quality 和工程文化都會往上提升。做得不好,就是浪費時間還傷感情。

記住幾個核心原則:

  1. Author:讓你的 PR 好 review——小、清楚、有 context
  2. Reviewer:從架構到細節三層看,comment 要有前綴和原因
  3. 兩邊都是:假設善意、對事不對人、說謝謝
  4. 團隊:漸進式導入、自動化能自動化的、追蹤數據持續改善

最後,Code Review 的終極目標不是「抓 bug」,而是讓團隊裡每個人都能寫出更好的 code。這是一個學習的過程,不是考試。

好的 Code Review 文化,是一個團隊最有價值的無形資產之一。