踩煞車才能開更快:Matt Pocock 的 PR 瓶頸解法

來源與導言

本文整理自 Matt Pocock 在 AI Engineer 大會的演講 Fixing the PR Bottleneck — Matt Pocock, AIHero。Matt Pocock 是 TypeScript 社群的知名講師,目前經營 AI Hero,並維護一套開源的 Claude Code/Agent skills(aihero.dev/skills)。

他的核心論點只有一句話:AI 讓 PR 數量暴增,但 PR 審查(Pull Request Review)沒有跟著變快,所以瓶頸從「寫程式」移到了「審程式」。解法不是審更快,而是在人工審查之前堆疊好幾層「煞車」,讓真正需要人看的東西變少、變簡單。

這篇不是逐字翻譯,而是依照他的論述結構重新整理,並補上專有名詞的英文對照與必要的背景說明。

軟體工廠需要煞車

「軟體工廠」(Software Factory)是當下最紅的流行語。Pocock 的定義很具體:以前所有工作都由人類發起,現在部分工作改由 agent 發起。例如 Sentry 抓到一個錯誤,就自動開一個 agent 去重現或修復;PlanetScale 回報資料庫慢查詢(slow query),就自動觸發另一條流程。這些都是確定性的程式碼在觸發,不是人。

這些機制是油門(accelerators)。它們把更多程式碼推進工廠。但如果只有油門沒有煞車,結果就是他所謂的 slop cannon——一台垃圾砲,噴出一堆品質低落、沒人有力氣審、甚至沒人想點開看的 PR。

更根本的問題是:codebase 就是 agent 工作的環境。如果 codebase 裡已經有壞程式碼,agent 會以它為範本,生出更多壞程式碼,形成軟體熵(software entropy)的惡性循環。

所以需要煞車(brakes):讓流程慢下來、拉高品質、避免 codebase 崩壞的機制。Pocock 提出三層煞車,由下而上疊成一個蛋糕:

  1. 自動化檢查(Automated Checks):確定性的檢查,lint、測試、型別檢查、程式碼品質指標。
  2. 自動化審查(Automated Review):由 agent 閱讀程式碼,抓出測試沒抓到的問題,審視結構。
  3. 人工審查(Human Review):人類看 PR。

他的主張很反直覺:踩這三層煞車,反而能開得更快。因為更快的速度意味著更多 PR,而目標是靠前兩層讓第三層的人工審查變快、變輕。第一原則是 stop the slop:你交出去的程式碼品質越高,人工審查就越少,需要介入的次數就越少。

第一層:自動化檢查便宜,但會說謊

自動化檢查(Automated Checks)是從 1950 年代就有的東西:lint、測試、型別檢查(type checking)、程式碼品質指標。它們每次執行結果都一樣。

它們最大的優點是便宜。不像自動化審查要花 token,也不像人工審查要花人力,它們只花 CPU 週期。就算 agent 寫出 bug 被測試抓到、再花 token 去修,那也是花得很值得的 token。既然便宜,就應該大量堆疊——Pocock 認為大多數團隊用得太少,也用得不夠有創意。

但檢查會說謊(checks can lie)。CI 綠燈不代表程式碼可以 merge。所以上面兩層(自動化審查與人工審查)本質上是測謊器(lie detectors),專門抓自動化檢查裡的謊言。他舉了三種 agent 實際寫出來的說謊測試。

1. 套套邏輯測試(Tautological Tests)

只是把實作重述一遍的測試。他說 Opus 5 對這種測試上了癮。實作是:

const X_POST_CHARACTER_LIMIT = 280;

測試是:

expect(X_POST_CHARACTER_LIMIT).toBe(280);

這種測試是結構敏感(structure-sensitive)的:它緊緊綁死系統內部的實作細節。你不能改常數值,測試會壞;你連把常數改名都不行,測試一樣壞。它測的不是行為,是結構。

2. 讀原始碼的測試

更離譜的例子:要驗證 UI 上「影片區塊」出現在「內容計畫區塊」之後。測試不渲染畫面,而是直接把模組的原始檔讀進記憶體,用字串搜尋找到 contentPlan 和 videos 的位置,然後斷言後者在前者之後。只要改一下原始碼的排版,測試就壞了。

3. 不可能失敗的測試(Tests That Cannot Fail)

濫用 mock 的人都會覺得熟悉。例如 useAudioBoost 函式內部用到瀏覽器的 AudioContext API。測試裡直接把 AudioContext 整個 stub 成假物件。問題是 AudioContext 有複雜的錯誤模式(error modes),在特定條件下會失敗。全部 stub 掉之後,測試永遠不會碰到這些錯誤,而 production 一定會。

重點:AI 不是故意作弊

AI 並不是在惡意寫爛測試。它只是照著指令,寫出太貼近結構、而不是真正執行程式碼的測試。既然檢查可以被「善意地」作弊,問題就變成:**怎麼讓自動化檢查更難作弊?**做到了,檢查的品質就提高,整體的品質門檻(quality bar)也跟著提高。

用 codebase 設計讓檢查更難作弊

Pocock 最推崇的解法是:用 codebase 設計(codebase design)把爛檢查設計掉。這個想法來自 John Ousterhout 的《A Philosophy of Software Design》中的深模組(Deep Modules)概念。

一個模組有兩個部分:上方的介面(interface)和下方的實作(implementation)。比較兩種模組:

模組 A(深模組)模組 B(淺模組)
介面很小,只有幾個函式很大,一堆函式可以呼叫
實作很大,藏在介面後面很小,每個函式各自做一點點事
對測試的影響測試只能打介面,結構敏感的測試自然變少測試很容易戳到實作細節

深模組把複雜行為藏在簡單介面後面。測試如果只能透過那個小介面進出,就很難寫出綁死內部結構的測試。所以你的工作是逼 agent 只能用那個小介面,不讓它伸手進實作去測奇怪的細節。

一套共用語言

如果你去查「怎麼組織 codebase」,會找到 20 種方法,而且全部都叫 DDD。團隊真正需要的是一套一致的語言來討論結構。Pocock 的 codebase design skill 定義了三個詞:

  • Locality(區域性):相關的程式碼是否集中在一起?在一個模組做小改動時,漣漪會擴散多遠?
  • Leverage(槓桿):深模組帶來的好處——呼叫端(caller)呼叫一個簡單的函式,就得到大量價值。
  • Seam(接縫):可以替換或隔離行為的邊界。他半開玩笑地說自己「在 seam 變潮之前就在用了」。

Locality 和 Leverage 對 codebase 好,對 agent 也好,因為 agent 找東西更快、改東西更安全。

對應的 skill

他有一個 skill 可以掃描任何 codebase——他的說法是「你見過最爛的 vibe-coded codebase」也行——產出一份 HTML 報告,列出所有加深模組的機會(opportunities for deepening modules),每一項都有 before/after 對照:減少重複、合併成一個深而可測的模組。你可以直接照著實作。

第二層:不要把 coding standards 塞進 implement agent

講了一堆高大上的編碼標準(coding standards),要怎麼確保 agent 真的照做?怎麼讓它寫深模組、寫好測試,而不是寫套套邏輯測試?Pocock 說,大多數人這裡都做錯了。

他的第一條建議:不要把 coding standards 放進實作 agent(implement agent)。

實作是超載的(Implementation is overloaded)

想像實作 agent 的單一上下文視窗(context window)要容納的工作:

  1. 探索(explore):找到要改的程式碼在哪。
  2. 實作(implement):真的去改檔案。
  3. 除錯(debug):跑自動化檢查,驗證改動真的能動。

這已經是很多工作了。如果再把一整套 coding standards 壓上去,它的表現會變差。這就是他要你建立的心智模型:實作是超載的。

審查是欠載的(Review is underloaded)

相對地,看看審查 agent(reviewer agent)要做的事:它收到一份 diff,所以已經知道程式碼在哪;它需要做一點探索,取得更寬的上下文、理解周邊程式碼;但它不需要實作,也不需要除錯。工作量少得多,所以是欠載的。這表示你可以塞一大堆 coding standards 進去,它會做得比實作 agent 好很多。

關鍵設計:審查 agent 跑在子代理(sub-agent)裡,有自己獨立的上下文視窗和預算。它收到 diff,讀取 repo 裡一個叫 coding-standards.md 的檔案(你自己寫、自己客製),然後檢查程式碼是否符合。

寫好程式碼的兩段式流程

這很不舒服,因為我們都希望第一次就產出好程式碼。但 Pocock 把它看成兩段式流程:

階段Agent目標對應的 TDD 概念
第一段Implement讓它能動(make it work)Red → Green
第二段Code Review讓它變好(make it good),套用 coding standardsRefactor

對老派開發者來說,這就是 Red-Green-Refactor:用一個上下文視窗做 red-green,再用另一個上下文視窗做 refactor。他說這在自己身上非常成功。

實務上的結論:coding standards 不要放在全域範圍(global scope),不要放進 AGENTS.md,因為那會淹沒實作 agent——它可能讀、可能不讀。放進 coding-standards.md,只讓審查 agent 讀。

一句話總結他的態度:Stop trying to one-shot good code. 不要再試圖讓實作 agent 一次就完美。

自動化審查的兩個實務建議

不要外包自動化審查

當天很多人跟 Pocock 說:「我們用第三方服務,Cursor Bugbot、CodeRabbit 之類的。」他自己曾經努力做過一個通用的 code review skill,要能抓所有 bug、做安全審查。結論是非常非常難,因為你會卡在兩難:

  • 做得太通用:只會給你一堆跟你的情境無關的誤報(false positives)。
  • 做得太特定:例如專抓 TypeScript 的問題,那 Rust 團隊就不能用。

所以他的建議是:不要外包自動化審查,自己建(don’t outsource automated review, build your own)。隨時間累積你自己的 coding standards,在團隊內共享。如果你手上有一些沒人讀的文件,正好,把它們變成 coding standards 放進來。

審查者應該 commit,而不是 comment

很多人對自動化審查 agent 的直覺是:它讀程式碼,然後在 PR 上留評論。

Pocock 指出這樣做的後果:審查 agent 其實是在幫人類審查者製造更多工作。人類還得讀完一堆冗長的評論,再逐一判斷「這個要改嗎?那個要改嗎?」。

正確做法是審查者應該 commit(the reviewer should commit)。它發現問題就直接修,直接改動程式碼。這樣等人類來審查時,看到的是一個已經很漂亮的成品。如果它對某個地方真的有疑問,當然可以留評論,但預設行為應該是 commit,不是 comment。

第三層:讓人工審查更友善的 PR skill

跑完自動化檢查、再用自動化審查確認檢查沒說謊之後,最後一哩是:怎麼讓 PR 對人類審查者最友善?Pocock 正在做一個新的 PR skill(演講時尚未釋出)。他說自己想了很久都找不到最佳實踐,最後發現最好的做法就是「把大家最好的點子全部偷過來」。一個好的 PR 描述(PR body)建立在三個原則上。

原則一:不是每個審查都同等重要

一旦理解這點,就能把精力集中在真正重要的審查上。怎麼分類?借用 AWS 的術語:單向門(one-way door)與雙向門(two-way door)。

雙向門(Two-way door)單向門(One-way door)
定義merge 之後可以輕易 revert一旦 merge 就很難或無法回頭
例子大多數的 PR誤寄信給 6 萬名使用者、昂貴的 migration、資料遺失
審查強度稍微看一下就好拚命審(review the hell out of it)

這是軟體工程師相對於土木工程師的奢侈:土木工程師蓋錯了,橋就塌了;我們大多數時候可以 revert。但要小心,一個看起來很簡單的改動也可能是單向門。

與此相關的是爆炸半徑(blast radius):這個 PR 會出什麼錯?如果出錯了,有多糟?

所以他的每個 PR 底部都有一段 merge danger 摘要,例如「雙向門,爆炸半徑局部」。看到這個,就知道不用花太多注意力,稍微審一下就好。

原則二:用 pseudo code 和圖說明 PR 在做什麼

審查者首先要搞懂這個 PR 到底在做什麼。他試過很多方法,最有效的是 pseudo code。

這裡他特別感謝 Dex Horthy 在 HumanLayer skills repo 裡的 show me skill:它幾乎捨棄所有文字,改用圖片和圖表來呈現,讓人更容易掌握「改了什麼」和「為什麼改」。你會得到標準的 Mermaid 圖和 UML 序列圖,也會得到非常簡單的示意圖——例如一張圖就看出「這是個 CLI,新增了一個指令,上面多了兩個 flag」。他說這種小摘要的效果「很難高估」。目標是:讓理解 why 的速度快到極限。

原則三:你審的不只是 code,而是產生 code 的系統

這一點引出下一節的 Retro skill。

Retro skill:永遠不要寫同一條評論兩次

現在的開發流程已經高度流線化,整個團隊圍繞著同一批 skill 檔案、同一批 steering files 在協作。換句話說,我們是在一起為 agent 建造一個工作環境。所以 Pocock 說:產生程式碼的流程,跟程式碼本身一樣重要。你做人工審查時,審的不只是這段 code,而是產生這段 code 的系統。

背後的原則是:你永遠不想寫同一條評論兩次(you never want to write the same comment twice)。你不想在兩個 PR 裡抓到 agent 犯同樣的錯。

那要靠什麼機制讓人工審查「有意義」?這就是新的 Retro skill(retro 即 retrospective,回顧)。

它怎麼運作

你餵給它一個範圍,可以是:

  • 單一的 agent session
  • 一個 PR 加上產生它的 session
  • 過去一週所有的 PR 和所有的審查

它會做一次回顧,然後建議新的自動化檢查與 coding standards,讓下一次更好。

它還會看什麼

Retro 不只建議檢查和標準,還會檢視幾個通常很難從外部除錯的面向:

面向它問的問題
自動化檢查(Automated checks)這次審查抓到的問題,能不能變成一條確定性的檢查?
Coding standardscoding-standards.md 該新增什麼?
導航指標(Navigation pointers)agent 找資訊順不順?能否在 AGENTS.md 加個指引讓它下次更快找到?
工具經濟(Tool economy)session 裡用的工具,能否換成更省 token 的用法?
膨脹(Bloat)steering files 或 skills 是否太肥、太亂,導致結果變差?能否重新整理?

複利效應

這就是複利效應(compounding effect):每做一次人工審查,就讓下一次人工審查的品質更高、工作量更少。人工審查不再是消耗,而是對系統的投資。

結語:把人工審查變成可選項

把三層放在一起看,Pocock 的整套策略是:

  1. 堆疊自動化檢查,而且用 codebase 設計讓它們更難被作弊。
  2. 自建自動化審查,用獨立的子代理套用 coding-standards.md,直接 commit 修正。
  3. 讓人工審查盡可能輕、簡單、甚至可選:雙向門不必每個都審,單向門每個都要審。
  4. 用 Retro 把每次審查的學習回灌到系統,讓下一次更輕。

他的 skills 都在 aihero.dev/skills,演講當週預計釋出 1.3 版。

幾個值得帶走的觀點

  • 檢查會說謊,所以上層是測謊器。這個框架把三層的角色講得很清楚:不是三道一樣的關卡,而是下層產出、上層驗證。
  • 實作超載、審查欠載。這是整場最有操作價值的洞察。它解釋了為什麼把一堆規則塞進 AGENTS.md 常常沒效,也給了一個簡單的結構性解法。
  • Red-Green-Refactor 的 agent 版本。用兩個 context window 分別負責「能動」與「變好」,對熟悉 TDD 的人來說非常自然。
  • 審查者要 commit,不要 comment。這一條直接對準「自動審查反而製造更多工作」的常見痛點。
  • 單向門/雙向門 + 爆炸半徑。這是把審查精力分級的實用工具,不需要任何 AI 也能立刻用。
  • 你審的是系統,不是 code。這和敏捷回顧(retrospective)的精神完全一致:不只修這一次的問題,而是修產生問題的流程。

發表迴響

探索更多來自 轉念學 - 敏捷三叔公的學習之旅 的內容

立即訂閱即可持續閱讀,還能取得所有封存文章。

繼續閱讀