本文深入探討了高效代碼審查的思維模型,通過聚焦風險,提升審查效率,并且有助于高效緩解項目風險。原文:The Mental Model That Made Code Reviews 10x Faster

不要追求完美,而是關注風險
以前代碼審查總讓我筋疲力盡。一個 PR 要花 30 分鐘,留下 15 條評論,卻還是覺得漏掉了什么重要問題。提交者越來越抵觸審查,我也累得不行,完全沒效率可言。
后來一位技術主管教了我另一種審查思路,用一個問題改變了一切。
我不再問「這段代碼有什么問題?」,而是問「這個 PR 里風險最高的地方是什么?」
突然間,代碼審查從 30 分鐘縮短到了 10 分鐘。評論少了,但每條都切中要害。奇怪的是,提交者開始感謝我的審查,不再跟我爭論。
完美審查的問題
我以前總想找出所有問題:每個風格問題、每個潛在改進、每個微小優(yōu)化。審查清單通常是這樣的:
- 「這個變量應該用 const,不該用 let」
- 「考慮把這塊提取成輔助函數(shù)」
- 「小問題:這里多了空格」
- 「這段可以寫成更函數(shù)式的風格」
- 「你有沒有考慮用 Map 代替對象?」
- 還有 12 條評論……
你猜結果怎么著?提交者要么忽略大部分反饋,要么花幾個小時處理雞毛蒜皮,反而把我埋在第 8 條評論里的真正 bug 給漏掉了。
問題不在于我挑錯了,而在于我審查時把所有問題都看得同等重要。
事實上它們根本不一樣。

基于風險的框架
這個思維模型是:每個代碼變更都有風險等級。你的工作是識別并解決風險,而非追求完美。
致命風險 —— 很可能導致生產(chǎn)環(huán)境問題:
- 安全漏洞
- 可能造成數(shù)據(jù)損壞
- 競態(tài)條件
- 關鍵路徑缺少錯誤處理
- 破壞公共 API 的變更
高風險 —— 可能導致生產(chǎn)環(huán)境問題:
- 大規(guī)模場景下的性能問題
- 缺少數(shù)據(jù)庫遷移
- 業(yè)務邏輯錯誤
- 破壞現(xiàn)有功能
中風險 —— 可能導致問題或技術債務:
- 錯誤信息不清晰
- 缺少調試日志
- 代碼難以維護
- 過度耦合會影響未來變更
低風險 —— 代碼風格和微小改進:
- 變量命名
- 代碼組織
- 微小性能優(yōu)化
- 遵循模式保持一致性
我以前的審查全都是低風險評論,純粹是吹毛求疵。
轉變:我從致命風險和高風險開始看。如果找到了這些問題,在解決之前,其他一切都不重要。
3 分鐘掃描法
現(xiàn)在我首先會做一個快速的 3 分鐘掃描,專門尋找暗示高風險的特定模式:
沒有遷移的數(shù)據(jù)庫變更:
// 紅旗:模式更改,但沒有遷移
class User {
email: string;
emailVerified: boolean; // 新字段,遷移在哪里?
}
沒有錯誤處理的外部 API 調用:
// 紅旗:沒有重試,沒有超時,沒有錯誤處理
async function syncUser(id) {
const data = await externalAPI.getUser(id)
return db.users.update(id, data)
}
認證/授權變更:
// 紅旗:認證邏輯修改
if (user.role === 'admin' || user.isOwner) { // 這里以前是'與'嗎?
allowAccess();
}
可能處理大量數(shù)據(jù)的數(shù)組操作:
// R紅旗:沒有分頁,可能有數(shù)百萬條記錄
const users = await db.users.findAll();
return users.map(formatUser);
如果發(fā)現(xiàn)任何上述問題,審查就只關注這個,其他問題都先放一放。
兩評論規(guī)則
我只允許自己留兩種評論:
1. 阻塞評論 —— 合并前必須解決:「如果兩個用戶同時編輯,這會導致數(shù)據(jù)丟失。我們需要樂觀鎖?!?/p>
2. 僅供參考 —— 需要知道,但不阻塞合并:「FYI: 我們在 utils.js 里有一個類似的函數(shù),對空值的處理方式不同,可以考慮對齊一下?!?/p>
沒有「考慮一下」這種評論,沒有「也許」評論,沒有「你怎么看?」這種評論。
要么重要到需要阻塞合并,要么就只是提供信息。
這迫使我必須有明確立場。如果我都不愿意為此阻塞 PR,這事兒真有那么重要嗎?
那個被漏掉的 bug 的故事
這個方法曾經(jīng)救過我們一次。我當時在審查一個支付處理的 PR,改動了 200 多行代碼。作者做了很多重構:重命名變量、提取函數(shù)。有很多地方可以評論。
放在以前,我會留下 20 條關于代碼風格和組織的評論。
但現(xiàn)在,我做了 3 分鐘掃描,發(fā)現(xiàn)了這個:
async function processRefund(orderId, amount) {
const order = await getOrder(orderId)
await stripe.refund(order.chargeId, amount)
await db.orders.update(orderId, { status: 'refunded', refundedAmount: amount })
return { success: true }
}
看起來沒問題,對不對?但風險在這里:如果 Stripe 調用成功,但數(shù)據(jù)庫更新失敗怎么辦?
我們已經(jīng)退了款,但數(shù)據(jù)庫不知道。客戶拿到了錢,我們卻沒有記錄。這就是致命風險。
我的整個審查就一條評論:「如果 Stripe 退款成功后數(shù)據(jù)庫更新失敗,我們會虧錢。需要用事務處理或者添加對賬機制?!?/p>
提交者用分布式事務包裝修復了這個問題,添加了冪等性,還加了對賬任務。
一條評論,就可能避免了數(shù)千美元的退款損失。
那個 PR 的其他一切 —— 變量命名、函數(shù)提取、代碼組織 —— 根本不重要。
我現(xiàn)在不再評論的內(nèi)容
我不再評論這些:
linter 能抓到的代碼風格問題。 如果團隊用了 Prettier 或 ESLint,相信它們,不要做人類 linter。
主觀改進。「這段可以更函數(shù)式」這種話毫無幫助。要么有具體問題,要么就沒有問題。
沒有數(shù)據(jù)支撐的性能優(yōu)化。 除非你能證明這是瓶頸,否則優(yōu)化都是過早的。
過度的抽象爭論。 如果代碼能用且清晰,抽象層級就沒問題。
個人偏好。 我更喜歡 Map 而不是對象。挺好。但如果別人用對象,也完全沒問題。
放下這些,我的審查時間減半,而且審查結果有用多了。
「LGTM」的驚人力量
當 PR 沒有重大風險時,我開始只用「LGTM」批準。
一開始,我覺得這樣不對。我總得找點什么評論來體現(xiàn)我的價值吧?
但結果是:PR 合并速度變快了。作者能更快得到反饋。而且當我真的留下評論時,他們開始相信這些評論確實重要。
上個月我給出的最棒的審查就是一句話:
「LGTM —— 實現(xiàn)干凈,測試覆蓋率好,沒有明顯風險?!?/p>
這個 PR 一小時后就合并了。沒有來回拉扯,沒有無謂爭論,直接上線。
相比之下,一周前我在一個類似 PR 上留了 12 條小評論,結果我們爭論變量名稱就花了三天才合并。
讓一切聚焦的問題
寫任何評論之前,我都會問:「如果保持原樣上線,最糟會發(fā)生什么?」
如果答案是:
- 「生產(chǎn)環(huán)境崩潰」 → 阻塞 PR
- 「我們會在凌晨 2 點被叫醒」 → 阻塞 PR
- 「靜默數(shù)據(jù)損壞」 → 阻塞 PR
- 「用戶會看到錯誤」 → 阻塞 PR
- 「代碼稍微沒那么干凈」 → 批準合并
這就過濾掉了我以前會評論的 80% 內(nèi)容。
什么時候需要徹底審查
有些時候還是需要慢下來,一絲不茍:
對安全敏感的代碼。 認證、支付、個人身份信息處理 —— 這些需要深度審查。我會追蹤每一條路徑,檢查每一處驗證,確認每一個權限檢查。
核心基礎設施變更。 數(shù)據(jù)庫 schema 變更、API 契約變更、部署配置 —— 這些影響方方面面,值得仔細審查。
新團隊成員寫的代碼。 不是因為他們技術不行,而是因為他們還不了解我們的坑。這是教學時間。
看不懂的變更。 如果你理不清邏輯,這就是一個信號。要么請求澄清,要么讓他們簡化。
但普通功能開發(fā)?認證中間件重構?修復管理后臺的 bug?快速掃描風險,批準,繼續(xù)下一個。
引以為傲的一次審查
有人提交了一個 PR,要用 WebSocket 添加實時更新。500 多行新代碼。放在以前,我會花一個小時逐行檢查。
這一次,我用 3 分鐘掃描聚焦于:
- 連接斷開了怎么處理?
- 服務器重啟會發(fā)生什么?
- 消息保序嗎?
- 我們會持久化任何內(nèi)容嗎?
- 故障模式是什么?
找到了兩個致命問題:
「如果服務器重啟,客戶端不會重連。它們就會一直停在那里顯示過期數(shù)據(jù)。我們需要帶指數(shù)退避的自動重連?!?/p>
「消息沒有持久化到任何地方。如果客戶端消息到達時不在線,他們就永遠看不到了。我們需要消息隊列或事件日志?!?/p>
這些都是阻塞問題。其他一切 —— 代碼結構、命名、模式 —— 都沒問題。
審查花了 15 分鐘,避免了兩起生產(chǎn)事故。
模式識別游戲
做了幾百次審查后,你會開始立刻識別風險模式:
「碰了數(shù)據(jù)庫,碰了外部 API」 → 檢查事務邊界和故障處理。
「新增環(huán)境變量」 → 檢查是否有文檔,是否有合理的默認值。
「修改錯誤處理」 → 檢查錯誤是否仍然被日志記錄/監(jiān)控。
「并發(fā)原語(鎖、原子操作、通道)」 → 檢查死鎖和競態(tài)條件。
「日期/時間處理」 → 檢查時區(qū)處理。
「刪除代碼」 → 檢查是否真的沒人用,會不會破壞功能。
「配置變更」 → 檢查向后兼容性。
這種模式匹配讓審查變得飛快。我不需要逐行閱讀,只需要掃描我知道有風險的模式。
好審查的標準
我最近做過的最好的代碼審查都有這些共同點:
- 一到兩條聚焦的評論,而不是一長串清單
- 清晰解釋風險,而不只是說「這錯了」
- 盡可能給出具體建議
- 快速反饋 —— 當天完成審查,通常幾小時內(nèi)
- 信任作者 —— 假設對方能力足夠,不故意找茬
說實話,最好的審查往往就是一句:「看起來不錯,上線吧。」
實踐中的思維模型
我現(xiàn)在實際的審查流程是這樣的:
1. 閱讀描述(1 分鐘) 這是要做什么?為什么要做?這個區(qū)域的風險等級是什么?
2. 掃描關鍵模式(2 分鐘) 數(shù)據(jù)庫變更?API 調用?認證?并發(fā)?外部依賴?
3. 必要時深入(5-10 分鐘) 如果發(fā)現(xiàn)風險,徹底理解它。追蹤代碼路徑,想清楚各種故障模式。
4. 評論或批準(1 分鐘) 要么清晰說明理由阻塞合并,要么批準繼續(xù)下一個。
總計:根據(jù)復雜度和風險,每次審查 5-15 分鐘。
對比以前的我:每次 30-45 分鐘,留下 15 條評論,大部分都不重要。
思維轉變
思維轉變就是:不要再追求代碼完美,而是努力防止事故。
你的工作不是抓到所有問題,而是抓到重要問題。
代碼審查不是為了代碼質量,而是為了緩解風險。
當我明白這一點后,審查變得輕松多了,也有效多了。
現(xiàn)在我打開 PR,只會問一個問題:「這里風險最高的是什么?」
其他一切都是噪音。
本文由mdnice多平臺發(fā)布