跳到主要內容
wen aidev

Kiro SDD 入門(三):為什麼 AI 修 bug 越修越壞

7 分鐘po-wen

你叫 AI 修一個 bug,它把半個 codebase 重構了。Bug Fix Paradox 的核心不是 AI 不會修,而是沒人告訴它哪裡不能動。這篇講 Kiro 的 Bugfix Spec 怎麼解決這個問題。

目錄

系列文章地圖

點節點可以直接跳到那一篇。

前兩篇講了 Specs 基礎跟 PBT,這篇要講一個用 AI 修 bug 的人幾乎都踩過的坑。

上一篇:


你一定遇過這個情況

你叫 AI 修一個 bug,它交回來的 patch 你一看:

  • 重構了三個 helper function
  • 加了一堆 if (x == null) 的防禦性檢查
  • 寫了一堆本來就會過的測試
  • 順便改了幾個根本沒壞的地方

你只是想修一個小 bug,結果 AI 給你一個大改動,改完之後原本正常的功能反而壞了。

Kiro 官方把這個現象叫 Bug Fix Paradox。


為什麼會這樣

問題出在你跟 AI 之間缺少一個共識:哪裡要改、哪裡不能動。

你心裡知道「我只是要修這個 edge case,其他地方不要動」,但你沒有把這件事寫下來。AI 沒有讀心術,它只看到「這裡有 bug,修掉」,然後用它覺得最安全的方式去修,於是加 guard clause、做重構,改動範圍越來越大。

Kiro 官方部落格引用 Stack Overflow 的〈State of AI vs. Human Code Generation Report〉:AI agent 加 guard clause 和防禦性錯誤處理的機率,是人類的將近兩倍。人類遇到 null 會先問「為什麼這裡是 null?」,AI 直接加 if (x == null) 就繼續走了。而且跟 AI 來回對話越多次,它越容易偏離原本的意圖。


Kiro 的解法:修之前先把三件事寫清楚

Kiro 的 Bugfix Spec 在修 code 之前,會先產出一份文件,把三件事寫清楚。

Bugfix Spec 把所有輸入切成兩塊:會觸發 bug 的條件 C,以及 C 以外、行為必須維持原樣的其他輸入。

bst.delete(root, key)
C=bug 條件:刪除有兩個子節點的節點,而且右子節點沒有左子樹。
0
crash
1
修好
2
行為被改
3
維持
沒劃邊界的修法
直接叫 AI 修。C 裡的 crash 修好了,但 AI 順手改了 C 以外會走到的程式碼,也沒人先記下那些輸入原本的行為。
點任一列,看它在 Bugfix Spec 裡對應的條款。

1. 現在錯在哪(Current Behavior)

不是「有時候會怪怪的」,是具體寫出什麼條件下會觸發、觸發之後會發生什麼錯誤。

比如一個二元搜尋樹(BST)的刪除功能有 bug:

WHEN 刪除一個有兩個子節點的節點,而且右子節點沒有左子樹
THEN 系統會 crash,報 AttributeError

2. 修完要怎樣(Expected Behavior)

修完之後的正確行為,寫成明確的條件:

WHEN 刪除一個有兩個子節點的節點,而且右子節點沒有左子樹
THEN 系統應該正常刪除,用右子節點的值取代被刪除的節點

3. 哪裡不能動(Unchanged Behavior)

這段是最重要的,也是最多人跳過的。明確列出哪些功能必須維持原樣:

WHEN 刪除葉節點或只有一個子節點的節點
THEN 系統應該維持原本的行為,正常移除節點

你把這三段 review 完,Kiro 才會開始動手。


Kiro 修 bug 的完整流程

跟一般 AI 不一樣,Kiro 不會看到 bug 就直接跳去寫 patch,它分四步走。

第一步:先證明 bug 存在

Kiro 會根據「現在錯在哪」寫測試,跑在還沒修的 code 上。這些測試應該要 fail。如果 pass 了,代表 bug 描述有問題,要先回去修正。

第二步:記錄正常行為的 baseline

Kiro 會根據「哪裡不能動」寫測試,一樣跑在還沒修的 code 上。這些測試應該要 pass,這就是修完之後的對照基準。

第三步:修 code

有了前兩步的測試當護欄,Kiro 才開始修。

第四步:重跑兩組測試

  • 第一組(bug 測試):現在應該要 pass,代表 bug 修好了
  • 第二組(正常行為測試):應該還是 pass,代表沒有壞其他東西

如果 bug 測試修完還是 fail,代表 root cause 判斷錯了,要回頭重新分析,不能硬修。如果正常行為測試 fail,代表改太多了,Kiro 會縮小 patch 範圍。

整套流程照先寫測試、先確認問題、再修、再驗證的順序走,確認問題之前不動 code。

同一個 BST 刪除 bug 用兩種修法各跑一次,差別在 AI 動了哪些行、哪些測試變紅。

情境對演 · 有無 Bugfix Spec
bst.py · delete()12 行
1def delete(node, key):
2 if node is None: return None
3 if key < node.key: node.left = delete(node.left, key)
4 elif key > node.key: node.right = delete(node.right, key)
5 else:
6 if node.left is None: return node.right
7 if node.right is None: return node.left
8 succ = node.right.leftbug
9 while succ.left: succ = succ.leftbug
10 node.key = succ.key
11 node.right = delete(node.right, succ.key)
12 return node
1 · 現在錯在哪
—
2 · 修完要怎樣
—
3 · 哪裡不能動
—
Bug 測試 · 現在錯在哪—
Baseline · 哪裡不能動—
選一種修法

什麼時候該用 Bugfix Spec

不是每個 bug 都需要開 Bugfix Spec:

  • 適合用:複雜 bug、root cause 不明確、改動可能影響其他地方、之前修過但又 regression 的
  • 不需要用:5 分鐘能修完、只改 1-2 行、你很確定不會影響其他地方的小 bug

坑點提醒: 最容易被跳過的是「哪裡不能動」那段。很多人覺得「我只是修一個小 bug,不用寫那麼多」,但偏偏就是那些你覺得不會動到的地方最容易被 AI 順手改掉。先花幾分鐘把最重要的不變行為列出來(安全相關的、對外 API、資料一致性),後面省的時間遠比你想的多。


參考

  • https://kiro.dev/blog/bug-fix-paradox/
  • https://kiro.dev/docs/specs/bugfix-specs/
  • https://kiro.dev/docs/specs/correctness/

支持作者 ☕

台灣用戶:

透過 LINE Pay 支持

國際用戶:

透過 Ko-fi 支持

相關文章

留言討論

Facebook 留言