我原本想反駁 Michael Amachree(@dev_michael)的〈AI 讓我成了更差的 code reviewer〉。結果我數了自己 204 個守門檢查,其中 89% 從來沒被要求證明自己「真的會失敗」。

Michael 寫了一段我看了就停不下來的內容:AI 沒有讓我成為更差的寫 code 人員,反而讓我成了更差的 reviewer。以下這個數字比他的論點還糟:在我各個 repository 裡 204 個會做出結論的自動化檢查中,只有 22 個能證明自己有失敗的能力。那是 11%。另外 89% 從來沒有被拿已知錯誤輸入測試過一次。它們是綠燈。至於它們是因為一切都沒問題才是綠燈,還是因為根本抓不到任何問題才是綠燈——上週的我根本說不出來。而我就是寫這些檢查的人。

我實際上怎麼算的

先定義一下,這樣你可以拒絕它,或直接拿去用。

帶結論的守門檢查(conclusion-bearing guard),是指任何會讀取原始碼、設定檔或系統狀態,並對其做出主張的測試。不是「這個函式會不會回傳 4」,而是像「沒有任何 workflow 會透過網路下載它的快取」、「每個頁面都通過同樣的 quarter filter」、「這個 feature flag 和已部署的規格一致」。這些測試是在代替人類 reviewer。

負對照(negative control),是把已知錯誤的輸入餵給那個 guard,並確認它會在預期原因下被拒絕。我們的慣例是在測試名稱裡標記 KONTROLLE:

計數方式很機械:三個 repository 裡共有 204 個 guard 檔案,其中 22 個至少有一個 control probe,總共 54 個 probe。這個計數器只是個代理值——它是用標記去算的,所以沒標記的 control 和誤判的 guard 檔都會讓真實數字上下浮動幾個點。不過整體形狀不會改變:我的大多數 reviewer,從來沒有被 review 過。

三個綠燈卻失明的檢查,一個普通的週間

這不是理論。以下三件事都在過去七天內發生在我身上,而且是在正式的生產工具裡。

那個死於自己藥方的部署閘門。 某個 pipeline 步驟原本就是要抓一種靜默失敗模式——缺少工具時,退回成空結果。它用 node -e 來解析 health response。偏偏部署執行環境裡沒有 Node。連續六次部署都以 exit 127 失敗——這個用來防止缺少工具的檢查,竟然在「缺少工具」時自己失敗了,結果整整六個小時都沒部署出去。這個步驟在 review 時一直是綠的,因為從來沒有人在它實際執行的地方跑過它。

那個把自己成果丟掉的收割器。 一個自動化工作從公開 repository 收集資料,並以 exit code 判定每次執行的結果。某次執行寫入了七筆完全正確的紀錄,接著遇到一個非致命警告,然後以非零狀態結束。這台機器把自己已完成的工作記成「失敗,稍後重試」——因為中斷但有部分結果(interrupted-with-partial-results)沒有任何表示方法,只有成功與失敗。之所以發現,是因為結果檔就在磁碟上,旁邊就是那個否認其存在的 exit code。

那個把錯的 500 給配對上的模式。 一個錯誤分類器用樣式 50[024] 尋找伺服器錯誤——它看的是整段輸出中的任意位置。結果它配對到了「4258 of 5000 quota points remaining」裡面的「500」,把一次成功的執行分類成伺服器失敗。它讀到的每個欄位都是真的,只是它在回答另一個問題。

三個不同的系統。只有一種共同形狀:這個檢查盯著的是傳訊者——exit code、pattern、status——而真正重要的 artifact,講的是另一個故事。

這跟 AI 讓你成為更差 reviewer 有什麼關係

我認為 Michael 那篇文章真正更狠的地方就在這裡。

AI 把我的工作內容搬了家。以前我大多數時間在產出 artifact,只有一小部分時間在驗證它們。現在是 agent 幫我產出大部分 artifact,而我的工作就是驗證。這表示我真正的 codebase——我的判斷實際上會透過它被送出去的那個地方——就是那 204 個 guard。

而這個 codebase 所接受的標準,是我在應用程式碼裡會直接拒絕的:沒有測試覆蓋率(11%)。沒有 reviewer 的 review。綠燈是預設狀態,沉默被算作成功。

當 Michael 說 AI 讓他成了更差的 reviewer,我會把這句話再說得更尖銳一點:AI 把我們全都升級成 reviewer,而我們卻沒有測試 reviewer。 弱點不是 model。真正的弱點是那個無法被否證的綠色勾勾。

這一週活下來的規則

上面所有內容可以濃縮成我們現在機械化套用的一句話:

看 artifact,不要看傳訊者。

exit code 是傳訊者。摘要是傳訊者。agent 自己的「done」是傳訊者。綠色徽章是傳訊者。artifact 才是 diff、磁碟上的檔案、實際回應的 body、資料庫裡的那一列。當傳訊者與 artifact 不一致時,artifact 才是對的——而且一個只會讀傳訊者的檢查,無論它多綠,都應該被視為未驗證。

對 guard 的推論是:綠色的零,是檢查能給出的最危險答案。「找不到違規」和「根本無法找到違規」會產生完全相同的輸出。只有負對照能把兩者分開。

算出你自己的比例(60 秒)

這段你不必先相信我也能用。把它丟到你的 repo root——它會計算讀取原始碼或狀態的測試檔,以及其中有多少帶有標記過的負對照(把 marker 調成你的慣例即可):

// count-controls.mjs — node count-controls.mjs
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";
const files = [];
(function walk(d) {
  for (const n of readdirSync(d)) {
    if (n === "node_modules" || n === ".git" || n === "dist") continue;
    const p = join(d, n);
    statSync(p).isDirectory() ? walk(p) : /\.test\.(t|j)sx?$/.test(n) && files.push(p);
  }
})(".");
let guards = 0, withControl = 0, probes = 0;
for (const f of files) {
  const t = readFileSync(f, "utf8");
  if (!/readFileSync|readdirSync|execSync/.test(t)) continue; // 「讀取狀態」代理
  guards++;
  const n = (t.match(/KONTROLLE|negative.control|can.?not.?find/gi) ?? []).length;
  if (n) withControl++;
  probes += n;
}
console.log(`${guards} conclusion-bearing guard files · ${withControl} with a negative control (${guards ? Math.round(100 * withControl / guards) : 0} %) · ${probes} probes`);

如果你的數字超過 30%,我真的很想知道你是怎麼做到的——這就是我希望下面大家討論的重點。

我在寫這篇文章時,兩次成了笑點

寫這類文章有一條規則是要主動修正自己,所以:

在建立這篇文章資料來源的功能時,我的等價性測試剛好差了 0.25,而 bug 出在我的測試,不是程式碼:min-max spreading 會把一整欄 0 變成一整欄 0.5,還會加上一個常數。我做了一個回答錯問題的 probe,而且是在測量完全同一種失敗類型的過程中。

同一個小時裡,還有一次 push 帶著紅色測試出去了——因為 npm test | grep 會把測試的 exit code 用 grep 的 exit code 取代掉。我的 pipeline 讀的是傳訊者;真正的 artifact——失敗的測試——就明明白白躺在那裡。

那個叫你要測試 reviewer 的人,當晚兩次都沒有測試自己的 reviewer。這不是反諷。這是基準發生率,也就是為什麼慣例比紀律更可靠。

這不能證明什麼

一位開發者、三個 repository、一週時間——這是一個案例系列,不是抽樣。11% 是基於標記的近似值。而且我還沒有證明提高 falsifiability coverage 之後會改善下游結果;我只能證明,在 11% 的情況下,我分不出工作中的 guard 和裝飾用的 guard。至於真正有意義的數字是 30% 還是 80%,我還不知道——我們正在把這個比例往上拉,邊做邊量。

另外也有一個合理的反對意見:負對照本身也是會腐化的測試。沒錯。但一個腐化的 control,當 guard 改變時,下一次會大聲地失敗——這種不對稱性,正是它們值得寫的原因。

所以:你的比例是多少?更有趣的是——你 pipeline 裡那個最綠、而你現在開始懷疑它其實從來就不可能失敗的檢查,是哪一個?


我在做 cachly —— 透過 MCP 為 AI coding assistants 提供記憶。ChatGPT 和 Claude 會記得你的對話;cachly 記得你的系統:你修過的 bug、你為什麼選擇 Postgres、那個總是壞掉的部署步驟——以及它和哪個更早的決策互相矛盾。你使用的每個 assistant 都讀同一份記憶,而每一條教訓都會帶著學會它的人的名字——這樣就沒人需要學第二次。

歐盟託管,免費方案:cachly.dev


原文出處:https://dev.to/heinrichneb/ai-promoted-every-developer-to-reviewer-nobody-tested-the-reviewer-m4h


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝12   💬4  
210
🥈
我愛JS
💬1  
6
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登