用 /grill-with-docs 把高鐵早鳥票「問」出驗收條件

需求文件不是寫出來的,是被問出來的

高鐵早鳥票的規則只有四段話,但真的要把它做成一個功能,至少藏了六個沒人講清楚的決定。「乘車日前 5 日」到底是哪一天?65 折賣完了、客人一次要買三張,要給 8 折還是原價?去回程要分開買,那系統要擋還是要提示?這些問題不會在你寫 User Story 時自己冒出來,只會在上線後變成客訴。

傳統做法是開一場需求澄清會議,PO、開發、測試坐下來一條一條對。這沒有錯,但很花時間,而且會議上大家常常「以為」自己聽懂了。AI 時代有另一種做法:讓 AI 當那個最煩人的測試人員,一題一題拷問你,直到每個分支都有答案為止。

/grill-with-docs 就是做這件事的工具。它是 Matt Pocock 在 mattpocock/skills 裡最受歡迎的技能之一,底層是兩個可組合的技能:

  • /grilling:拷問引擎。針對一個計畫或決定,一次只問一個問題,每題附上它建議的答案,等你回覆後才問下一題,直到設計樹的每個分支都收斂。
  • /domain-modeling:領域建模。把你說出口的每個名詞拿去跟專案的 GLOSSARY.md 對照,模糊的用語會被挑戰,並用具體情境逼出邊界案例;確定下來的術語會即時寫回詞彙表,難以回頭的決定則記成 ADR(Architecture Decision Record)。

/grill-with-docs 把兩者合在一起:一邊拷問,一邊把共識落地成文件。對做 Specification by Example 的人來說,這剛好就是「用範例澄清需求」的前半段,而且產出物可以直接接到驗收條件。

這篇文章用高鐵早鳥票當例子,走一遍完整流程:從安裝、餵入官方規則、被拷問、整理業務規則,到最後產出 Given/When/Then 的驗收條件。

準備工作:三個指令就能開打

第一步,安裝技能。 在 Claude Code 裡執行:

claude plugin install mattpocock-skills@claude-plugins-official

裝成 plugin 的好處是會自動更新。如果你想要可以自己改的檔案版本,改用 npx skills@latest add mattpocock/skills,它會把 SKILL.md 複製進 .claude/skills/。兩種擇一就好,兩種都裝每個技能會出現兩次。

第二步,初始化專案。 進到你的專案目錄,執行一次 /setup-matt-pocock-skills。它會問你三件事:用哪個 issue tracker、triage 時用什麼標籤、文件要存在哪裡。對這篇文章來說,最後一題最重要,它決定 GLOSSARY.md 和 ADR 要放在哪個資料夾。我習慣放在 docs/。

第三步,把規則餵進去。 開一個新的 Claude Code 對話,輸入 /grill-with-docs,接著貼上要討論的東西。我貼的是高鐵官網的早鳥優惠說明原文,再加一句我的目的:

/grill-with-docs

我要為訂票系統設計「早鳥優惠」的訂票與計價功能。以下是公告規則,請拷問我,直到可以寫出驗收條件為止:

早鳥優惠依本公司公告之開放訂位日起限量發售,最晚發售至乘車日(含)前 5 日截止。購票範例:乘車日為 9/1,於 8/28(含)前訂位者,有機會買到早鳥 65 折、早鳥 8 折或早鳥 9 折之車票;於 8/29~9/1 訂位者,皆為原價。
早鳥優惠僅適用於標準車廂對號座單程全票,如欲購買去回程車票者,請分別購票。車票票面將標示旅客之身分證字號或護照號碼末 5 碼,限本人搭乘。
早鳥優惠提供指定車次早鳥 65 折、8 折或 9 折限量優惠;65 折車票銷售完畢即改發售 8 折 → 8 折銷售完畢即改發售 9 折 → 9 折銷售完畢即提前截止並改發售原價車票。
單筆訂購多張車票時,剩餘早鳥優惠座位數足夠其需求張數,方能提供優惠價格。

兩個小技巧。第一,貼原文,不要自己先摘要。你摘要的過程就是在偷偷做決定,拷問的價值正是把這些決定攤開來。第二,明講你要的終點是「驗收條件」,它會把問題往可驗證的方向收斂,而不是漫談架構。

如果專案是全新的,它會先探索一下目錄,發現沒有 GLOSSARY.md,然後告訴你它會邊問邊建。有既有的程式碼的話,它會先讀程式碼,能從程式碼回答的問題就不會來煩你。

拷問實錄:四段話,問出七個決定

下面是我這場拷問的濃縮版。每一題它都會先給建議答案,我只要回「對」或是改掉它。真實對話大約二十題,我挑七題最能看出它價值的。

第 1 題:「乘車日前 5 日」和範例對不上,你要信哪一個?

它第一題就抓到文字和範例的矛盾。乘車日 9/1,往前數 5 天是 8/27,但範例說 8/28(含)前都買得到。它的建議:以範例為準,定義一個術語「早鳥截止日 = 乘車日減 4 天」,並且在詞彙表註明「公告文字的『前 5 日』是把乘車日當天算作第 1 日」。我同意。這個術語之後所有問題都會用到,不用再每次解釋。

第 2 題:截止的時間點是 8/28 的幾點?看訂位時間還是付款時間?

我原本沒想過這題。它建議:以「訂位成立時間」為準,截止時間是早鳥截止日當天 23:59:59,系統時區用台灣時間。付款逾時導致訂位取消,重新訂位時用新的訂位時間重算。我同意,這條直接進詞彙表。

第 3 題:「指定車次」是什麼意思?沒有早鳥額度的車次怎麼辦?

它用情境逼我:「同一天的 0603 車次有早鳥額度,0605 沒有。旅客 8/20 訂 0605,系統要顯示原價還是顯示『早鳥已售完』?」我回答:顯示原價,不出現任何早鳥字樣。它追問額度是按車次設定還是按日期設定,我說按車次,而且每個車次三個折扣各有獨立席次數。這裡冒出第二個術語「早鳥額度」:某車次在某折扣下的剩餘席次。

第 4 題:65 折賣完換 8 折,那「9 折賣完提前截止」是什麼狀態?

這題在釐清狀態機。它畫出四個狀態:65 折中、8 折中、9 折中、已截止。前三個依序往下掉,不會回頭。「已截止」有兩種進入方式:9 折額度歸零,或是過了早鳥截止日。兩種進入後的行為一樣:只賣原價。我確認後它問「退票會不會讓額度回補、狀態倒退?」我說這個版本先不處理退票回補,記成待辦。它沒有硬要我現在決定,只是註記在 ADR 的「未決事項」。

第 5 題:一次買 3 張,65 折剩 1 張、8 折剩 5 張,給什麼價?

這是整場最值錢的一題。公告只說「剩餘早鳥優惠座位數足夠其需求張數,方能提供優惠價格」,但沒說「剩餘」是指目前折扣層的剩餘,還是所有早鳥層加總的剩餘。它列出三個解法:

  1. 同單同價,往下找第一個夠的折扣層:3 張都 8 折。
  2. 混價:1 張 65 折加 2 張 8 折。
  3. 當前層不夠就全部原價。

它的建議是第一個,理由是票面顯示和退票計算都最單純,而且符合「同單同價」的直覺。我同意。它判斷這是難以回頭的決定(牽動計價、票面、退票三個模組),所以寫了一份 ADR:docs/adr/0001-multi-ticket-same-tier.md。

第 6 題:「去回程請分別購票」是擋還是提示?

它問:「旅客在同一筆訂單選了去程和回程,系統要拒絕、還是拆成兩筆、還是只有單程才顯示早鳥選項?」我回答:訂票流程本來就是單程一筆,來回票是另一種票種,早鳥只出現在單程的購票流程。它接著確認:來回票、商務車廂、自由座、孩童票、敬老票、愛心票,全部不適用早鳥。我補了一條:團體票也不適用。

第 7 題:「限本人搭乘」要怎麼驗?系統要做什麼?

它問系統層面的責任邊界:訂票時是否必須輸入每位旅客的證件號碼?票面顯示末 5 碼是訂票系統還是出票系統的責任?我決定:訂票時每張票都必須填證件號碼,缺一張就不能用早鳥價;票面末 5 碼由出票模組負責,本版本驗收只到「訂票資料有存到」為止。

問到這裡它說設計樹已經沒有未解分支,問我要不要收尾。收尾時它做了三件事:把 GLOSSARY.md 更新成六個術語、把第 5 題寫成 ADR、把第 4 題的退票回補列入未決清單。

拷問後的產出:六個術語、八條規則、一份 ADR

拷問結束後,GLOSSARY.md 多了這六個術語。之後不管是寫驗收條件、寫程式、還是下一次跟 AI 對話,大家用的都是同一組字:

術語定義
早鳥票標準車廂、對號座、單程、全票,且訂位成立時間在早鳥截止時間(含)之前,並成功取得早鳥額度的車票
早鳥截止日乘車日減 4 天。例:乘車日 9/1,早鳥截止日為 8/28
早鳥截止時間早鳥截止日 23:59:59(台灣時間),以訂位成立時間判定
早鳥額度某車次在某折扣層(65 折、8 折、9 折)的剩餘席次數;三層各自獨立設定
折扣層早鳥的三個折扣等級,依 65 折 → 8 折 → 9 折 順序供應,不回頭
提前截止車次的 9 折額度歸零後,即使尚未到早鳥截止時間,該車次只賣原價

業務規則整理成八條,每條都能對應到至少一個驗收條件:

編號規則來源
BR-1訂位成立時間在早鳥截止時間(含)之前,才有機會取得早鳥價;之後一律原價公告第 1 段、第 1–2 題
BR-2只有標準車廂、對號座、單程、全票適用;商務車廂、自由座、來回票、孩童、敬老、愛心、團體票一律不適用公告第 2 段、第 6 題
BR-3早鳥額度按車次設定,沒有設定額度的車次只賣原價,且不顯示早鳥字樣第 3 題
BR-4折扣層依 65 折 → 8 折 → 9 折 供應,前一層額度歸零才供應下一層公告第 3 段
BR-59 折額度歸零即提前截止,只賣原價公告第 3 段、第 4 題
BR-6單筆多張時同單同價:從目前折扣層往下找第一個剩餘額度 ≥ 需求張數的層;都不夠則全部原價公告第 4 段、第 5 題(ADR-0001)
BR-7早鳥票每張都必須填寫旅客證件號碼,缺一張即不能以早鳥價成立訂位公告第 2 段、第 7 題
BR-8取得早鳥價時,該折扣層的額度即扣減需求張數第 3、5 題

未決事項一條:退票是否回補早鳥額度、狀態是否可倒退,留到下一版決定。

ADR-0001 的核心只有三句:單筆訂單所有車票使用同一折扣層;選層規則是從目前層往下找第一個足夠的層;理由是票面、計價、退票三個模組都不用處理混價。這份 ADR 之後誰想改成混價,先來看這三句。

驗收條件:從八條規則長出來的 Given/When/Then

拷問結束後,我請它接著產出驗收條件。因為詞彙表已經在手上,它寫出來的 scenario 用字跟規則表完全一致,不用再對齊。下面依規則分組,每組先給正常路徑,再給邊界。共用的背景條件放在最前面。

Feature: 早鳥優惠訂票與計價

  Background:
    Given 車次 0603 乘車日為 9/1,標準車廂對號座原價 1490 元
    And   車次 0603 的早鳥額度為 65 折 2 張、8 折 5 張、9 折 10 張
    And   系統時間以台灣時間判定

BR-1 早鳥截止時間

  Scenario: 早鳥截止日當天 23:59:59 仍可取得早鳥價
    Given 目前時間為 8/28 23:59:59
    When  旅客訂購車次 0603 單程全票 1 張並填妥證件號碼
    Then  票價為 65 折 969 元

  Scenario: 早鳥截止時間後一秒即為原價
    Given 目前時間為 8/29 00:00:00
    When  旅客訂購車次 0603 單程全票 1 張
    Then  票價為原價 1490 元
    And   畫面不顯示任何早鳥字樣

  Scenario: 付款逾時重新訂位以新的訂位時間判定
    Given 旅客於 8/28 23:50 訂位 1 張 65 折票但付款逾時,訂位已取消
    And   目前時間為 8/29 00:10
    When  旅客重新訂購車次 0603 單程全票 1 張
    Then  票價為原價 1490 元

BR-2 適用票種

  Scenario Outline: 不適用票種一律原價
    Given 目前時間為 8/20 10:00
    When  旅客訂購車次 0603 <票種> 1 張
    Then  票價為該票種原價
    And   畫面不顯示任何早鳥字樣

    Examples:
      | 票種         |
      | 商務車廂對號座 |
      | 標準車廂自由座 |
      | 孩童票        |
      | 敬老票        |
      | 愛心票        |
      | 團體票        |

  Scenario: 來回票流程不出現早鳥選項
    Given 目前時間為 8/20 10:00
    When  旅客進入來回票購票流程並選擇車次 0603
    Then  票價為原價
    And   畫面不顯示任何早鳥字樣

BR-3 指定車次

  Scenario: 未設定早鳥額度的車次只賣原價
    Given 車次 0605 乘車日為 9/1,未設定任何早鳥額度
    And   目前時間為 8/20 10:00
    When  旅客訂購車次 0605 單程全票 1 張
    Then  票價為原價 1490 元
    And   畫面不顯示任何早鳥字樣

BR-4、BR-5 折扣層遞補與提前截止

  Scenario: 65 折售完自動改供應 8 折
    Given 目前時間為 8/20 10:00
    And   車次 0603 的 65 折額度已為 0
    When  旅客訂購車次 0603 單程全票 1 張
    Then  票價為 8 折 1192 元

  Scenario: 8 折售完自動改供應 9 折
    Given 目前時間為 8/20 10:00
    And   車次 0603 的 65 折與 8 折額度均為 0
    When  旅客訂購車次 0603 單程全票 1 張
    Then  票價為 9 折 1341 元

  Scenario: 9 折售完即提前截止,早鳥截止時間前也只賣原價
    Given 目前時間為 8/20 10:00
    And   車次 0603 的三個折扣層額度均為 0
    When  旅客訂購車次 0603 單程全票 1 張
    Then  票價為原價 1490 元
    And   畫面不顯示任何早鳥字樣

BR-6、BR-8 單筆多張同單同價

  Scenario: 目前折扣層足夠時全部以該層計價
    Given 目前時間為 8/20 10:00
    When  旅客一次訂購車次 0603 單程全票 2 張
    Then  2 張票價均為 65 折 969 元
    And   車次 0603 的 65 折額度變為 0

  Scenario: 目前折扣層不足時往下找第一個足夠的層
    Given 目前時間為 8/20 10:00
    And   車次 0603 的早鳥額度為 65 折 1 張、8 折 5 張、9 折 10 張
    When  旅客一次訂購車次 0603 單程全票 3 張
    Then  3 張票價均為 8 折 1192 元
    And   車次 0603 的 65 折額度仍為 1,8 折額度變為 2

  Scenario: 所有折扣層都不足時全部原價
    Given 目前時間為 8/20 10:00
    And   車次 0603 的早鳥額度為 65 折 1 張、8 折 2 張、9 折 3 張
    When  旅客一次訂購車次 0603 單程全票 4 張
    Then  4 張票價均為原價 1490 元
    And   車次 0603 的三個折扣層額度均不變

  Scenario: 同一筆訂單不出現混價
    Given 目前時間為 8/20 10:00
    And   車次 0603 的早鳥額度為 65 折 1 張、8 折 5 張、9 折 10 張
    When  旅客一次訂購車次 0603 單程全票 3 張
    Then  訂單中不存在 65 折與 8 折並存的車票

BR-7 證件號碼

  Scenario: 任一張未填證件號碼即不能以早鳥價成立訂位
    Given 目前時間為 8/20 10:00
    When  旅客一次訂購車次 0603 單程全票 2 張,只填寫 1 位旅客的證件號碼
    Then  訂位不成立
    And   系統提示「早鳥票每位旅客均須填寫證件號碼」
    And   車次 0603 的早鳥額度不變

  Scenario: 早鳥票訂位資料保存每位旅客證件號碼
    Given 目前時間為 8/20 10:00
    When  旅客一次訂購車次 0603 單程全票 2 張並填妥 2 位旅客證件號碼
    Then  訂位成立
    And   每張車票的訂位資料均含該旅客證件號碼

數一下:17 個 scenario 對應 8 條規則,每條規則至少有一個正常路徑和一個邊界。其中「23:59:59」、「付款逾時重訂」、「65 折剩 1 張買 3 張」、「只填 1 位證件號碼」這幾個,都不是公告原文寫的,是拷問時被問出來的。沒有那場拷問,這些 scenario 多半會在上線後才以 bug 的形式出現。

心得:它不是幫你想,是逼你想

用過幾次之後,我對 /grill-with-docs 的定位是:它取代不了 PO,但它是一個不會累、不會給面子、而且每題都附建議答案的測試人員。幾個提醒:

建議答案不是正確答案。 第 5 題它建議「同單同價」,我同意是因為我知道票面和退票的複雜度;換一家公司,混價可能才是對的。每一題都要真的想過再按下同意,不然你只是把決定外包給 AI,拷問就失去意義了。

它的價值在「問」,不在「寫」。 驗收條件那一段,它寫得很順是因為詞彙表已經定好了。如果你跳過拷問直接要它寫 Given/When/Then,它一樣寫得出 17 個 scenario,但「23:59:59」和「65 折剩 1 張」那幾個大概率不會出現,因為它不知道哪裡有坑。

規則原文和範例矛盾時,它會逼你選邊。 第 1 題的「前 5 日」就是。真實專案裡這種矛盾很常見,而且常常兩邊都是對的,只是定義不同。拷問會把它變成一個明確的術語,寫進詞彙表,之後沒有人需要再猜。

適合用在哪裡。 新功能開工前、接手別人的系統要改動核心流程時、或是需求來自一份對外公告或法規文件時。這三種情境的共同點是:文字看起來完整,但沒人真的把所有分支走過一遍。

不適合用在哪裡。 已經有完整範例和驗收條件的功能,再拷一次只是浪費時間;還在探索「要不要做」的階段,拷問會太早把你推進細節。另外它預設專案裡有程式碼可以探索,純文件的討論可以用輕量版的 /grill-me,少了詞彙表和 ADR 的部分。

最後一個觀察。Specification by Example 的核心從來不是 Gherkin 語法,而是「用具體範例把模糊的規則逼出邊界」。這件事以前要靠三方會議,現在可以先讓 AI 把你拷一輪,帶著整理好的術語、規則和未決事項再去開會。會議會短很多,而且大家會發現,原來自己以為聽懂的地方,有一半其實沒懂。

參考來源

發佈留言

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

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

繼續閱讀