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,但我要自己驗證修改範圍、錯誤案例和原本正常功能。
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 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
這次的錯誤不是:
伺服器自己壞掉
而是:
送進來的資料格式不符合要求
例如:
due_date = not-a-date
應該要的是:
YYYY-MM-DD
所以比較合理的是:
400 Bad Request
可以先這樣理解:
500
→ 程式沒有處理住的錯誤
400
→ 使用者送來的請求不符合要求
如果規劃沒問題,我才會回:
這版規劃可以。
請只修改 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 說只改一個檔案,不代表我就直接相信。
修 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,只證明:
錯誤案例
被擋住了。
但還不知道正常功能有沒有被修壞。
所以還要測原本正常的案例。
再送:
due_date = 2026-10-10
應該:
正常新增
↓
寫進 MySQL
不能因為加了 try/except 之後,連正常日期都被擋住。
目前程式原本允許:
due_date = ""
最後變成:
None
所以修完後也要測:
due_date 留空
確認仍然可以正常新增。
資料庫應該是:
due_date = NULL
而不是:
400
因為同樣的日期解析程式也出現在:
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
不只是畫面要顯示錯誤。
資料庫也不能已經寫進去了。
目前 new_task() 是先解析日期,再:
task = Task(...)
db.session.add(task)
db.session.commit()
所以如果日期在前面就發生:
ValueError
還沒有走到:
db.session.commit()
這也是為什麼日期錯誤理論上不應該產生正常的新 Task。
測試時還是要自己確認。
Claude Code 修改完可能會說:
已修復。
甚至可能說:
已驗證。
但我要分清楚兩件事:
Claude 做的檢查
和:
我自己實際操作
不是同一件事。
今天我的驗證至少包含:
Postman
瀏覽器
MySQL
有需要時再看:
終端機
例如:
請不要只說「測試成功」。
請列出:
1. 測了哪個 URL
2. 用什麼 HTTP Method
3. 傳了什麼資料
4. 預期 Status Code
5. 實際結果
6. 有沒有確認 MySQL
這樣「已驗證」才比較有意義。
Day 18 還額外發現:
title = ""
可以繞過瀏覽器的:
required
直接存進資料庫。
今天先不一定修它。
但可以拿它來學另一件事:
修一個 Bug 時,不要因為順手看到第二個 Bug,就讓修改範圍一直膨脹。
今天原本的目標是:
日期格式造成 500
那就先把這件事處理完。
title 後端驗證先記在:
待處理問題
之後再決定什麼時候補。
例如它原本只需要:
app.py
但最後 diff 裡出現:
CSS 調整
HTML 排版
Model 重構
資料表變更
就代表它做了超出需求的事情。
這時不一定要全部接受。
可以直接要求:
請回復與這個 Bug 無關的修改。
這次只保留:
new_task()
edit_task()
日期格式錯誤處理。
我現在會避免:
幫我把日期 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 比較不容易自己延伸需求。
Day 18 的流程是:
重現
↓
讀 Traceback
↓
找原因
Day 19 再往後走一步:
重現 Bug
↓
確認原因
↓
要求 Claude 先規劃
↓
限制修改範圍
↓
讓 Claude 修改
↓
檢查它改了什麼
↓
重做原本錯誤案例
↓
測正常案例
↓
測邊界案例
↓
確認 MySQL
這才算完整。
如果 Day 19 修正成功,最後應該得到:
/tasks/new
錯誤日期
→ 400
/tasks/<id>/edit
錯誤日期
→ 400
合法日期
→ 正常新增 / 更新
空白日期
→ 正常,存 NULL
而且不能影響:
Task CRUD
狀態更新
狀態篩選
分類篩選
今天最大的差別不是:
我會叫 Claude 修 Bug 了
而是:
Claude 說修好了
≠
真的修好了
我要自己確認:
它改了哪些檔案
它有沒有超出範圍
原本的 Bug 能不能重現
錯誤案例有沒有被修掉
正常案例有沒有被改壞
資料庫結果對不對
所以現在我比較不會只看 Claude 最後一句:
已完成
而是把它當成:
現在輪到我驗收
Day 19 到這裡完成。
下一步,程式功能越來越多,app.py 也開始越來越長。
接下來會開始整理程式結構,但不是為了把架構弄複雜,而是讓後面的程式比較容易維護。
「先看修改計畫再動手」這步很值。補兩個我自己做這類修法會多驗的點:
400 要發生在任何寫入之前。edit_task() 如果先 UPDATE 了 title、status,才解析 due_date 然後 abort(400),使用者會看到錯誤,資料卻已經被改一半。建議把日期解析放在函式最前面,驗完才碰資料庫,測試時也在 MySQL 裡確認那筆資料沒被動到。
把 not-a-date 那個案例留成一個固定的自動測試(Flask 的 test client 一個 POST 就夠)。這個 Bug 是 Day 18 靠手動 Postman 才抓到的,之後再請 Claude 重構 app.py,沒有測試的話,這種修法很容易被悄悄改掉,沒人發現。