iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0
Claude AI

盡信 Claude,不如無 Code — 心法與全端實戰系列 第 8

Day 8 讓它來拷問我的規格:/grill-me 四輪、24 題,問到 frontier 清空

  • 分享至 

  • xImage
  •  

昨天讓 Plan Mode 對著一句需求提了七條假設、問了一題,然後說「沒有了」。今天換一個專門做這件事的工具,這次把規格直接交給它拷問 —— 看它是問得更狠,還是問出一樣的洞。

工具是 Matt Pocock 的 /grill-me。他那個 skills repo 七個多月從零到二十六萬星,而 /grill-me 是裡面很具代表性的一支:一個「relentless interview」,把你的計畫問到沒東西可問為止。


它是什麼:28 行的核心

/grill-me 本身只有 7 行,做一件事:呼叫另一支叫 grilling 的 skill。真正定義拷問規則的是 grilling,一共 28 行,核心規則有四條:

  1. 把你的計畫當一棵決策樹:每個決定底下,掛著依賴它才能成立的下一層決定。
  2. 一輪問完整個 frontier(前提已定案、現在就能問的那一排題):一次問完,每題附它的建議答案;你答完,它重算 frontier,問下一層。前提還沒定的題不會提前問。
  3. 事實它自己查,不問你:檔案系統裡查得到的東西,它派 sub-agent 去看,不拿來當問題。
  4. frontier 空了才算完,而且在你確認之前不准動手。

第 2 條和第 4 條,就是它跟 Plan Mode 的差別:Plan Mode 的「假設」是計畫的附錄,列完七條就交計畫;grilling 的問題本身就是產出,交計畫之前要先問到沒得問。

另外還有一支 /grill-with-docs:同樣的拷問,邊問邊產 glossary 和 ADR。先跑 /grill-me,後面再用它跑一次。

怎麼執行

乾淨 clone,放在原 repo 之外的目錄(Day 4 講的那個鄰居的洞),rm -rf .git、拿掉 devlog/(裡面有昨天 Plan Mode 的三版計畫,留著它就有答案可抄)。規格與非目標這次留著:昨天是讓它從一句需求問起,今天是讓它拷問寫好的規格,兩篇驗的東西不同。tests/overdue.test.js 那支紅測試也在 —— 它是 repo 裡唯一的一支測試,拷問過程中它也會讀。

需要的四支 skill 照 commit 抓進 .claude/skills/,然後:

claude -p "/grill-me 我要實作借書與還書這個 feature。規格在 docs/spec.md,非目標在 docs/non-goals.md,硬規則在 CLAUDE.md。請拷問這份規格。" \
  --permission-mode default --allowedTools "Read,Glob,Grep,Skill,Agent"

之後每一輪 --resume 接回,把我的答案原文貼給它。紀律跟昨天一樣:規格有的照規格答,沒有的當場決定並記下來

四輪,24 題

它給的 秒 / $
1 讀完七個檔,開場一句判斷,再 Q1–Q11 158 / 0.48
2 Q12–Q21,其中一題是追問我上一輪的答案 143 / 0.40
3 自己定了閘門的 API,Q22–Q24,整棵 24 個節點的設計樹 74 / 0.21
4 Frontier 清空」;接下來只改 spec、寫 tests/README.mdCLAUDE.md 一字不動、不寫 src/ 8 / 0.08

合計 24 題、$1.18、六分多鐘。跟昨天 Plan Mode 的三輪 $1.32 比:問題是七條假設的三倍多,錢還少 —— 因為它不寫計畫,只問。

開場那一句

它讀完規格、非目標、CLAUDE.md、紅測試,第一段不是問題,是判斷:

規格在「規則」層寫得很硬,但在「怎麼落地」層有幾個洞,而且有兩條規則彼此有張力

張力是 Q2:規格要 books.status 存成欄位、走白名單、有 CHECK;又要「有未歸還紀錄 ⇔ on_loan」被測試證明。這等於要維護兩份真相 —— 一致性得靠每次借還都在同一個交易裡同步動兩張表。它給三個選項、建議第三個:兩者都存,partial unique index 當第二道防線,「任何時候兩者不一致就是 bug,測試直接驗這條不變量」。這件事我寫規格時沒想過,昨天 Plan Mode 也沒提 —— 它的計畫做的就是第三個選項(兩張表同一交易、partial unique index 也有),做對了,但沒告訴我這裡有張力。

它問的題長什麼樣

第一輪 11 題,每題附建議答案。挑三題:

Q3:硬規則 2 說「不准在別處寫 status = '...'」,但 UPDATE books SET status = 'on_loan' 這行 SQL 本身就是寫 status。它提出落地法 —— 字面值只出現在 book-state.js,SQL 全部參數化,靜態檢查 grep status\s*=\s*' 只允許那一檔 —— 然後補一句:這是硬規則 2 唯一講得通的落地法;否則就得寫 trigger,而那會讓實作比題目本身還大。

Q5:規格決定 4「一人同一本書只能一筆未歸還」,在一書一冊之下是多餘的 —— 書已經 on_loan,同一人再借本來就會被擋,這條沒有獨立的失敗路徑。它還指出:規格「為什麼」那欄寫的是「讓同一人重複送出不污染併發測試」,那是測試設計的理由,不是業務理由。這個問題我寫規格時沒發現。

Q8GET /api/users/:id/borrows 未歸還那段要不要帶即時算的 overdueDays?它建議不帶(「要帶就是第二個真相」)。我答「帶」 —— 兩段用同一支純函式,它不是第二個真相,而是同一個快照在不同 now 下的讀數。

然後第二輪它追問了(Q14):history 那段的 overdueDays 讀存的欄位還是重算?如果重算,還書時押進去的 overdue_days 就變成「只寫不讀」。它說:「你的論點我同意,但 history 的 now 就是 returned_at,那個讀數已經在還書當下押進去了;再算一次不是錯,是多一個可以分岔的地方。」我改採它的:history 讀存的,active 現算。

24 題我照它的建議答了 23 題,唯一不同的那題被它追問後也改回去了。這不代表它每題都對,而是每題都先把選項和取捨排好,我只要判斷「這個場合適不適用」。

第二輪:上一層答完,下一層才問得出來

第二輪的十題沒有一題是第一輪能問的。Q12 問併發測試的閘門插在哪 —— 這題只有在 Q1 選了「測試用 Node 內建 node:sqlite、抽一層 DB 介面」之後才存在。因為 node:sqlite 是同步的,SQL 不可能交錯,閘門必須插在應用層的 await 點。它建議包住 adapter 的 batch(),N 個請求到齊才放行,src/ 零改動;並且說要先證明它會紅:對照組是一版「先 SELECT 再 UPDATE 不帶 WHERE」的 naive 借書,同一個閘門下應該出現 N 個 201 —— 「沒有紅過的閘門等於沒有閘門。」第三輪 Q22 又把這件事推一步:naive 對照組不要紅過就刪,留成永久測試,守的是閘門本身有沒有偵測 TOCTOU 的能力。

第四輪:「Frontier 清空。整棵樹 24 個節點全部有人裁決,沒有任何一個是我默默假設的。」

加強版:/grill-with-docs,邊問邊把檔寫出來

/grill-with-docs 本體只有一行:「Call the Skill tool twice, for grilling and domain-modeling.」拷問流程一模一樣,多掛的 domain-modeling(74 行)管三件事:你用了模糊的詞、或跟 glossary 打架的詞,它當場攔;詞一定案就寫進 CONTEXT.md(純 glossary,明講不准放實作細節);ADR 要三個條件同時成立才開 —— 難回頭、沒脈絡看不懂、真的有取捨 —— 少一個就不寫。

同一份規格、同一個起點再跑一次,這次 --allowedTools 多給 Write,Edit。五輪(四輪問、一輪摘要)、33 題、$2.10、十一分鐘。

它多問了什麼。 第一輪的 Q13 是 /grill-me 那次沒有的題型:規格裡 POST /api/borrowborrow_records 用 borrow,loan_policieson_loan 用 loan,中文又有「借閱」「借書」「借期」—— 要寫 glossary 得選一個。它建議中文正名「借閱」、英文以 borrow 為主,on_loanloan_policies 標成「契約遺留」、不再新造。第三輪 CONTEXT.md 寫出來 67 行,每個詞都附一行 Avoid:書不准叫副本、copy;讀者不准叫 patron、reader、會員;到期時刻不准叫 deadline、到期日。Q5 那條「一人一書多餘」也進了 glossary,條目就叫「單冊版的推論規則」。

ADR 的閘門真的有在擋。 它開了兩份:測試跑 node:sqlite 不跑 D1;借還兩筆寫入放同一個 batch、第二句用 WHERE changes() = 1 綁住第一句。然後明講三件不開:錯誤碼表、欄位命名、overdue_days 存不存 —— 「都是規格延伸或容易改的,不夠格」。第二份 ADR 那招是 /grill-me 那次沒出現的:上次的設計是輸家靠 unique index 拋錯、整批回滾;這次它自己把這個設計推翻 —— route 得解析錯誤訊息,而 D1 跟 node:sqlite 的錯誤格式不一樣 —— 改成正常路徑零例外,並在 ADR 裡承認「changes() 只在 node:sqlite 驗過,D1 上是假設」。

還有一個坑值得記:第二輪它要寫 CONTEXT.md-p 擋下,理由是「敏感檔案」。查了才知道是路徑不是檔名 —— 我把乾淨 clone 放在 ~/.claude/ 底下的暫存目錄,那整棵在非互動 session 裡都不給寫。搬出來、--resume 接回同一個 session 就好了。

33 題比 24 題多,但同一個模型跑兩次本來就不會一樣,這 9 題不能全部算在 domain-modeling 頭上;能確定是它帶來的,是 Q13 那種詞彙題,和拷問結束時桌上多出來的三份檔。

跟 Plan Mode 比

同一份需求、同一天、兩種問法:

Plan Mode(Day 7) /grill-me
給它什麼 一句需求(規格拿掉) 規格 + 非目標 + 硬規則 + 紅測試
輪 / 題 3 / 七條假設 + 1 題 4 / 24
成本 $1.32 $1.18
產出 197 行計畫 一棵設計樹(不寫計畫)
抓到規格自己的問題 0(它猜錯三處,是它沒讀到規格) 兩條規則的張力、決定 4 多餘、硬規則 2 的字面衝突
兩邊都碰到 overdueDays 要不要帶進 list、loan_policies 存快照、要不要開 src/db/
只有它問到 —— 併發閘門插哪、naive 對照組先紅再留成永久測試、D1 batch() 不能分支怎麼守、status code 表、時間字串怎麼比、GET 用 LEFT JOIN 一次查完
它沒問的 兩條 GET 的細節、一人一書(我補的) 非目標一條沒踩;也沒問任何「要不要加什麼」

最後一列是我覺得最重要的:24 題裡,沒有一題是在問「要不要加 X」。它問的全是「你寫的這些怎麼同時成立」。非目標清單把「加東西」的門關了,它就只往裡面問。

另一個差別是方向。Plan Mode 從一句需求往外推,問的是「我該做哪些」;grilling 從寫好的規格往裡鑽,問的是「這些對不對得起來」。所以它會問到規格自己的問題 —— 兩份真相、多餘的規則、寫得太硬落不了地的規則 —— 這些問題 Plan Mode 不會主動問:一方面它沒讀規格,另一方面它的目標是交出計畫,不是找出規格本身的問題。

差在哪

它沒有比 Plan Mode 聰明。同一個模型,同一份規格,差別不在模型,而在那 28 行規則:一輪問整個 frontier、每題附建議、事實自己查、問到空為止。Plan Mode 七條假設就交計畫,是因為它的目標是計畫;grilling 問了 24 題,是因為它的目標是問完。

但「問完」有價值的前提,是你答得出來。24 題我答了不到半小時,因為大部分是照規格念、少數當場決定。如果規格不存在,這 24 題會變成 24 個要你現場想的設計決定 —— 那就不是拷問,是它在替你設計。順序還是 Day 7 那句:先寫你的,再讓它問。


總結

/grill-me 是 28 行 prompt,做的事只有一件:不給計畫,只給問題,問到沒得問。四輪 24 題,$1.18,抓到我規格裡兩條規則的張力、一條多餘的規則、一條寫得太硬、字面上落不了地的硬規則 —— 三件事昨天 Plan Mode 都沒提:前兩件是它沒讀規格;第三件它的計畫其實用了同一套落地法,只是沒說這條規則字面上有衝突。

兩個工具不衝突。需求還是一句話的時候,Plan Mode 從它往外推;規格寫好了,grilling 往裡鑽。兩邊真正有東西可問的前提,都是你先寫出東西。要邊問邊留檔,換 /grill-with-docs:同樣的拷問,多一份 glossary,和幾份它認為夠格的 ADR。

這一篇留下的心法:

拷問的價值不在問了幾題,而在問的全是「你寫的這些怎麼同時成立」—— 而不是「要不要多做什麼」。後者靠非目標清單擋,前者靠有人肯問到底。

明天:測試是給 AI 的護欄,也是驗收標準。先寫一個會紅的測試,再讓它動手。


參考資料


上一篇
Day 7 先寫規格,再讓 Plan Mode 把它補完整
系列文
盡信 Claude,不如無 Code — 心法與全端實戰8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言