很多團隊導入 AI 之後,第一個反應是「請 AI 幫我寫 Gherkin」。結果得到一堆語法正確、內容空洞的場景,而且沒有人知道這些場景是不是產品真正想要的行為。問題不在 AI,而在跳過了 BDD 真正的核心:Discovery、Formulation、Automation 三個階段。AI 在每個階段都能幫上忙,但角色完全不同。
下面拿一個電商常見的功能來走一遍:購物車滿額免運費。
階段一:Discovery — AI 當「提問機器」,人負責決定
Discovery 的目的是在寫任何東西之前,先把需求裡的模糊地帶挖出來。傳統做法是三個朋友(PO、開發、測試)一起做 Example Mapping。這一步 AI 不能取代人,因為「什麼是正確行為」的答案在 PO 腦袋裡,不在模型裡。這就是 oracle problem:如果連驗收標準都讓 AI 決定,那後面 AI 寫的程式和 AI 寫的測試就會互相背書,沒有人在把關。
但 AI 很適合扮演那個「一直問為什麼」的討厭鬼。我把使用者故事丟給它:
身為顧客,當我的購物車金額達到 1,000 元時,我希望享有免運費,這樣我會更願意湊單。
然後請它列出「這個故事有哪些沒說清楚的地方」。它回了十幾個問題,挑幾個有價值的:
- 1,000 元是折扣前還是折扣後的金額?
- 剛好 1,000 元算不算達標?
- 使用優惠券後金額掉到 1,000 以下,免運費要不要取消?
- 預購商品或訂閱商品算進門檻嗎?
- 離島配送是否適用同樣門檻?
這些問題在 Example Mapping 的工作坊裡本來就會被問到,但 AI 可以在會前十分鐘先產出,讓三個朋友把時間花在決定答案上,而不是花在想問題上。工作坊結束後,我們得到的是人做的決定:
- 門檻以折扣後金額計算
- 剛好 1,000 元算達標
- 預購商品不計入
- 離島不在這次範圍(另開故事)
注意這一步的產出還不是 AC,而是「規則 + 具體例子 + 已決定的問題」。這才是 AC 的原料。
階段二:Formulation — AI 當「翻譯」,人負責審
Formulation 是把 Discovery 的例子轉成結構化、可讀、可執行的格式,通常是 Gherkin。這一步最適合 AI,因為它是機械式的轉換工作,而且輸入已經被人釐清過了。
我把工作坊的規則和例子餵給 AI,並附上兩個限制:每個場景只講一條規則、用業務語言而不是 UI 操作。它產出:
gherkin
Feature: 購物車滿額免運費 Rule: 折扣後金額達到 1,000 元即免運費 Example: 折扣後剛好達到門檻 Given 購物車商品小計為 1,200 元 And 使用了 200 元折價券 When 結帳計算運費 Then 運費為 0 元 Example: 折扣後低於門檻 Given 購物車商品小計為 1,100 元 And 使用了 200 元折價券 When 結帳計算運費 Then 運費為 80 元 Rule: 預購商品不計入免運門檻 Example: 預購商品撐起的金額不算 Given 購物車有一般商品 600 元 And 購物車有預購商品 500 元 When 結帳計算運費 Then 運費為 80 元
這裡人要做的是審查,而不是重寫。我通常檢查三件事:
- 每個例子是否對應到 Discovery 的一個決定? 例如「剛好 1,000 算達標」有沒有被一個例子精準覆蓋。AI 第一版把折價後 1,000 的例子寫成了 1,200,我要求它改成剛好 1,000,因為邊界值才是這條規則的重點。
- 有沒有 AI 自己「補」進來的規則? 它自作主張加了一個「VIP 會員 800 元免運」的場景,工作坊根本沒討論過。這種要直接刪掉,並且提醒 AI 不要發明需求。
- 語言是不是 PO 看得懂的? 如果出現「點擊結帳按鈕」「呼叫 shipping API」,就要改回業務語言。
審完之後,這份 Gherkin 就是 AC。它同時是需求文件、測試案例,也是後面自動化的規格。
階段三:Automation — AI 當「工人」,規格當「老闆」
Automation 是讓 Gherkin 變成可執行的測試,接上真正的程式碼。這一步 AI 的產能最驚人,但也最容易出事:如果前面兩步沒做好,AI 會很開心地把錯的規格自動化得很漂亮。
我把 feature 檔丟給 Claude Code,請它產出 step definitions。因為場景已經用業務語言寫好,它產出的 step 也自然對應到 domain 物件,而不是綁在 UI 上:
python
given(parsers.parse("購物車商品小計為 {amount:d} 元"))def cart_subtotal(cart, amount): cart.add_item(price=amount)given(parsers.parse("使用了 {amount:d} 元折價券"))def apply_coupon(cart, amount): cart.apply_coupon(amount)when("結帳計算運費")def calculate_shipping(cart, context): context["fee"] = ShippingCalculator().calculate(cart)then(parsers.parse("運費為 {fee:d} 元"))def assert_fee(context, fee): assert context["fee"] == fee
這時候測試是紅的,因為功能還沒寫。接著才請 AI 實作 ShippingCalculator,直到所有場景轉綠。順序很重要:先有人審過的規格,再有測試,最後才有實作。這樣 AI 寫出來的程式碼是被規格約束的,而不是規格反過來遷就程式碼。
三個階段,三種分工
| 階段 | AI 的角色 | 人的角色 | 產出 |
|---|---|---|---|
| Discovery | 提出問題、找出模糊處 | 做決定、定義正確行為 | 規則、例子、已釐清的問題 |
| Formulation | 把例子翻譯成 Gherkin | 審查、刪除 AI 發明的內容 | 驗收條件(AC) |
| Automation | 產生 step definitions 與實作 | 確認順序:規格→測試→程式 | 可執行的規格 |
AI 讓後兩個階段快了好幾倍,這反而讓第一個階段變得更重要。因為當產生程式碼幾乎免費時,瓶頸就移到了「我們到底想要什麼行為」。BDD 的三階段正好把這個問題留在人手上,把其他機械式的工作交給 AI。
這才是「用 AI 做 BDD」該有的樣子:不是讓 AI 替你想需求,而是讓你有更多時間去想需求。
發表迴響