iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Claude AI

從零開始 Claude Code:30 天打造 AI 任務管理 Web App系列 第 19 篇

[Day 19] 讓 Claude Code 幫我修 Bug,但不能只相信它說「修好了」

  • 分享至 

  • xImage
  •  

Day 18 我刻意測了幾種錯誤:

404
400
500
ValueError
資料庫連線錯誤

也真的看到:

ValueError: time data 'not-a-date'
does not match format '%Y-%m-%d'

這次我開始知道 Traceback 要怎麼看。

但接下來還有另一個問題:

Claude Code 說「已修復」,我要怎麼知道它真的修好了?

所以 Day 19 不只是「叫 Claude 修 Bug」。

今天真正要練的是:

讓 Claude Code 修 Bug,但我要自己驗證修改範圍、錯誤案例和原本正常功能。


今天拿哪個 Bug 來修?

Day 18 已經實際重現一個很適合拿來練習的 Bug。

目前新增、編輯任務都有:

due_date = (
    datetime.strptime(due_date_text, "%Y-%m-%d").date()
    if due_date_text
    else None
)

正常瀏覽器日期欄位會送:

2026-10-03

所以平常看起來沒問題。

但如果用 Postman 直接送:

due_date = not-a-date

就會:

ValueError
↓
500 Internal Server Error

這就是今天要修的主要 Bug。


今天先控制範圍

今天只處理:

錯誤日期格式
→ 不要變成 500

而且新增、編輯兩個地方都要一致。

今天不做:

分類 CRUD
搜尋
分頁
services/
routes/
大型重構
Claude API

Day 18 還額外發現:

後端沒有驗證 title 不可為空

這也是問題,但今天先不要一次把所有問題混在一起。

先把「日期格式造成 500」修乾淨。


第一步不是叫 Claude「幫我修」

我先把錯誤重現資訊整理清楚。

可以這樣告訴 Claude Code:

Day 18 已經實際重現一個 Bug:

POST /tasks/new
如果 due_date 傳入:

not-a-date

目前這段:

datetime.strptime(due_date_text, "%Y-%m-%d").date()

會丟出 ValueError,
最後變成 500 Internal Server Error。

edit_task() 也有相同的日期解析邏輯。

今天要修這個 Bug。

需求:

1. 錯誤日期格式不能再變成 500
2. new_task() 和 edit_task() 都要處理
3. 合法日期原本功能不能壞
4. 空白日期仍然要存成 None
5. 不重構整個 app.py
6. 不建立新的 service / route / utility
7. 優先使用最小修改
8. 請先分析:
   - Bug 原因
   - 需要修改哪些地方
   - 修改後預期回什麼 HTTP status
   - 驗證方式

先不要直接修改程式。

這次我不想讓 Claude 一看到 Bug 就直接動手。

先看它到底準備改什麼。


為什麼要先看修改計畫?

假設 Bug 明明只有:

日期格式不合法

結果 Claude 說要修改:

app.py
models/task.py
schema.sql
index.html
add_task.html
edit_task.html

那我就會先停下來。

因為這個問題的核心其實只有:

datetime.strptime(...)

最小修改應該集中在:

app.py

裡面的:

new_task()
edit_task()

如果修改範圍突然很大,就要先問:

為什麼這些檔案都需要修改?

今天希望怎麼處理錯誤日期?

目前:

datetime.strptime(due_date_text, "%Y-%m-%d")

格式錯誤會丟:

ValueError

所以最直接的做法就是:

try:
    due_date = (
        datetime.strptime(due_date_text, "%Y-%m-%d").date()
        if due_date_text
        else None
    )
except ValueError:
    abort(400)

意思是:

合法日期
→ 正常轉換

空字串
→ None

不合法日期
→ 400 Bad Request

而不是:

不合法日期
→ ValueError
→ 500

為什麼回 400,不是 500?

這次的錯誤不是:

伺服器自己壞掉

而是:

送進來的資料格式不符合要求

例如:

due_date = not-a-date

應該要的是:

YYYY-MM-DD

所以比較合理的是:

400 Bad Request

可以先這樣理解:

500
→ 程式沒有處理住的錯誤

400
→ 使用者送來的請求不符合要求

先讓 Claude 修改,再看它到底改了什麼

如果規劃沒問題,我才會回:

這版規劃可以。

請只修改 app.py。

new_task() 和 edit_task() 的 due_date 解析都要處理 ValueError。

錯誤格式請回 400。

不要重構其他程式,
不要修改其他檔案。

修改完成後告訴我:
1. 實際改了哪些行為
2. 如何驗證
3. 有沒有影響原本合法日期和空白日期

這一步很重要。

因為我不是只在等:

已修復

而是在等:

它到底改了什麼

修完第一件事:看修改範圍

如果 Claude Code 說:

只修改 app.py

我要確認真的只有:

app.py

被改。

如果突然多出:

models/task.py
templates/
schema.sql

就要問原因。

這是 Day 19 很重要的一個習慣:

AI 說只改一個檔案,不代表我就直接相信。


測試 1:原本會炸掉的錯誤案例

修 Bug 最重要的第一個測試,就是:

重做原本會出錯的操作。

Day 18 是用:

due_date = not-a-date

造成:

500

所以修完後,再用 Postman 測一次。

例如:

POST
http://127.0.0.1:5000/tasks/new

Body 使用:

x-www-form-urlencoded

例如:

title       Day 19 測試
description 錯誤日期測試
status      todo
category    學習
due_date    not-a-date

修正前:

500 Internal Server Error

修正後應該變成:

400 Bad Request

只看到 400 還不夠

如果錯誤資料會回 400,只證明:

錯誤案例

被擋住了。

但還不知道正常功能有沒有被修壞。

所以還要測原本正常的案例。


測試 2:合法日期

再送:

due_date = 2026-10-10

應該:

正常新增
↓
寫進 MySQL

不能因為加了 try/except 之後,連正常日期都被擋住。


測試 3:空白日期

目前程式原本允許:

due_date = ""

最後變成:

None

所以修完後也要測:

due_date 留空

確認仍然可以正常新增。

資料庫應該是:

due_date = NULL

而不是:

400

測試 4:編輯任務也要測

因為同樣的日期解析程式也出現在:

edit_task()

所以不能只測 /tasks/new。

還要測:

POST /tasks/<id>/edit

故意送:

due_date = not-a-date

也應該:

400

而不是:

500

這可以確認 Claude 沒有只修一半。


「修好了」至少要通過四種測試

今天這個 Bug 修完後,我會檢查:

錯誤日期
→ 400

合法日期
→ 正常

空白日期
→ 正常 / NULL

新增和編輯
→ 都有處理

如果只有:

/tasks/new

修好,但:

/tasks/<id>/edit

還會 500,那不能算完成。


還要確認沒有新增垃圾資料

Day 18 測試時曾經因為 curl 資料送錯,意外建立過測試資料。

所以這次 Postman 測試後,我還會回 MySQL 看:

SELECT * FROM tasks;

確認:

錯誤日期的請求

沒有偷偷新增一筆不完整 Task。

因為:

回 400

不只是畫面要顯示錯誤。

資料庫也不能已經寫進去了。


修 Bug 時順序也很重要

目前 new_task() 是先解析日期,再:

task = Task(...)
db.session.add(task)
db.session.commit()

所以如果日期在前面就發生:

ValueError

還沒有走到:

db.session.commit()

這也是為什麼日期錯誤理論上不應該產生正常的新 Task。

測試時還是要自己確認。


Claude Code 的「驗證」不等於我的驗證

Claude Code 修改完可能會說:

已修復。

甚至可能說:

已驗證。

但我要分清楚兩件事:

Claude 做的檢查

和:

我自己實際操作

不是同一件事。

今天我的驗證至少包含:

Postman
瀏覽器
MySQL

有需要時再看:

終端機

我現在會要求 Claude 說清楚它怎麼驗證

例如:

請不要只說「測試成功」。

請列出:

1. 測了哪個 URL
2. 用什麼 HTTP Method
3. 傳了什麼資料
4. 預期 Status Code
5. 實際結果
6. 有沒有確認 MySQL

這樣「已驗證」才比較有意義。


再檢查 Day 18 發現的 title 問題

Day 18 還額外發現:

title = ""

可以繞過瀏覽器的:

required

直接存進資料庫。

今天先不一定修它。

但可以拿它來學另一件事:

修一個 Bug 時,不要因為順手看到第二個 Bug,就讓修改範圍一直膨脹。

今天原本的目標是:

日期格式造成 500

那就先把這件事處理完。

title 後端驗證先記在:

待處理問題

之後再決定什麼時候補。


我會怎麼判斷 Claude 有沒有改太多?

例如它原本只需要:

app.py

但最後 diff 裡出現:

CSS 調整
HTML 排版
Model 重構
資料表變更

就代表它做了超出需求的事情。

這時不一定要全部接受。

可以直接要求:

請回復與這個 Bug 無關的修改。

這次只保留:
new_task()
edit_task()
日期格式錯誤處理。

修 Bug 的 Prompt 也要更精準

我現在會避免:

幫我把日期 Bug 修好。

改成:

目前已確認的 Bug:

POST /tasks/new 或 /tasks/<id>/edit
傳入 due_date=not-a-date 時,
datetime.strptime() 會丟 ValueError,
造成 500。

請用最小修改處理:

- 捕捉 ValueError
- 回 400
- 合法 YYYY-MM-DD 保持正常
- 空字串仍然是 None
- new_task 與 edit_task 都要處理
- 只修改 app.py
- 不重構
- 不修改其他功能

修改後請列出實際驗證方式。

這樣 Claude 比較不容易自己延伸需求。


今天的 Bug 修復流程

Day 18 的流程是:

重現
↓
讀 Traceback
↓
找原因

Day 19 再往後走一步:

重現 Bug
↓
確認原因
↓
要求 Claude 先規劃
↓
限制修改範圍
↓
讓 Claude 修改
↓
檢查它改了什麼
↓
重做原本錯誤案例
↓
測正常案例
↓
測邊界案例
↓
確認 MySQL

這才算完整。


今天實際要確認的結果

如果 Day 19 修正成功,最後應該得到:

/tasks/new
錯誤日期
→ 400

/tasks/<id>/edit
錯誤日期
→ 400

合法日期
→ 正常新增 / 更新

空白日期
→ 正常,存 NULL

而且不能影響:

Task CRUD
狀態更新
狀態篩選
分類篩選

Day 19 做完後

今天最大的差別不是:

我會叫 Claude 修 Bug 了

而是:

Claude 說修好了
≠
真的修好了

我要自己確認:

它改了哪些檔案
它有沒有超出範圍
原本的 Bug 能不能重現
錯誤案例有沒有被修掉
正常案例有沒有被改壞
資料庫結果對不對

所以現在我比較不會只看 Claude 最後一句:

已完成

而是把它當成:

現在輪到我驗收

Day 19 到這裡完成。
下一步,程式功能越來越多,app.py 也開始越來越長。
接下來會開始整理程式結構,但不是為了把架構弄複雜,而是讓後面的程式比較容易維護。


上一篇
[Day 18] 程式開始變多:錯誤處理與除錯
下一篇
[Day 20] 功能做完先整理:開始重構程式碼
系列文
從零開始 Claude Code:30 天打造 AI 任務管理 Web App 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
helenanova
iT邦新手 5 級 ‧ 2026-10-03 19:12:42

「先看修改計畫再動手」這步很值。補兩個我自己做這類修法會多驗的點:

  1. 400 要發生在任何寫入之前。edit_task() 如果先 UPDATE 了 title、status,才解析 due_date 然後 abort(400),使用者會看到錯誤,資料卻已經被改一半。建議把日期解析放在函式最前面,驗完才碰資料庫,測試時也在 MySQL 裡確認那筆資料沒被動到。

  2. 把 not-a-date 那個案例留成一個固定的自動測試(Flask 的 test client 一個 POST 就夠)。這個 Bug 是 Day 18 靠手動 Postman 才抓到的,之後再請 Claude 重構 app.py,沒有測試的話,這種修法很容易被悄悄改掉,沒人發現。

我要留言

立即登入留言