所有入門單元免費開放 · 課程內容持續更新中

用 Agent 做大型重構

Agent 寫程式很快,快到可以一口氣改壞四十個檔案。這堂課以「把記帳程式的 CSV 換成 SQLite」為例,教你用四個習慣安全地完成大型修改:先規劃、先補測試、小步前進、每步都留存檔點。

第 7.4 堂約 16 分鐘

前三堂你已經有了一套不錯的工作方式:Agent 平行開發、各自有工作區、CI 自動檢查。這套方式在處理「新增一個功能」「修一個 bug」時非常順。

但有一種工作特別容易出事:大型重構。

什麼是重構?為什麼特別危險?

重構(refactoring)是改變程式的寫法,但不改變它的行為。例如把重複的程式整理成函式、把一個太大的檔案拆開、把資料的存放方式從 CSV 換成資料庫。使用者用起來應該完全一樣,只是程式變得更好維護。

重構的危險在於它碰的地方很多。你說「把 CSV 換成 SQLite」,Agent 可能會改十幾個檔案,而且改得很快。如果中間有一個地方弄錯,測試全紅,你面對一大堆修改,根本不知道從哪裡查起。

左右對照:一次改完是從現在直接跳到目標,結果改了 40 個檔案測試全紅,不知道哪裡壞的,只能全部重來;小步前進是一階一階往上走,每一階測試都是綠的、都有 commit,出錯只要退回上一階

Agent 的速度是優點,但沒有煞車的速度很危險。這堂課教你裝上煞車。

接下來用一個具體的例子走一遍:前面單元做過的記帳程式 expense-app,目前用 expenses.csv 存資料,我們要把它換成 Python 內建的 SQLite 資料庫。

習慣一:先規劃,再動手

不要一開口就說「把 CSV 換成 SQLite」。先讓 Agent 只讀不改,把現況和步驟想清楚。

Claude Code 有一個計畫模式(plan mode):在這個模式下,它會讀檔案、提出計畫,但在你同意之前不會修改任何東西。在對話中按 Shift+Tab 切換,直到畫面下方顯示計畫模式;或者啟動時就指定:

claude --permission-mode plan

然後交代任務,並要求它把計畫寫下來:

可以這樣請 Agent 規劃重構

我想把 expense-app 的資料儲存方式從 expenses.csv 換成 SQLite(用 Python 內建的 sqlite3),使用者操作和輸出的結果要完全不變。

請先不要修改任何檔案。請:

  1. 找出所有讀寫 CSV 的地方,列出檔案和函式。
  2. 提出一份分成 5 到 8 步的計畫,每一步都要小到可以單獨跑測試確認,並說明每一步改哪些檔案。
  3. 標出哪一步風險最高、為什麼。
  4. 寫出「這次不做的事」,避免範圍越來越大。

我確認後,請把計畫存成 REFACTOR_PLAN.md。

讀它的計畫時,問自己三個問題:每一步做完,程式還能不能跑?每一步的修改,我讀得完嗎?真實資料 expenses.csv 會不會被動到?

一份好的計畫可能長這樣:

# 重構計畫:CSV 換成 SQLite

目標:資料改存在 expenses.db,使用者操作與輸出完全不變。
這次不做:不改畫面文字、不新增功能、不刪除 expenses.csv。

1. 補測試:替 storage.py 的公開函式寫測試,鎖住目前的行為
2. 整理入口:確認 main.py 只透過 storage.py 存取資料,沒有直接讀 CSV
3. 新增 sqlite_storage.py,提供和 storage.py 一樣的函式,並通過同一組測試
4. 寫一次性的轉檔程式:把 CSV 資料複製到 SQLite(先備份)
5. 切換:main.py 改用 sqlite_storage
6. 確認一週沒問題後,再移除舊的 CSV 程式碼

注意第 6 步:舊的不急著刪。留著它,萬一新的有問題,切回去只要改一行。

習慣二:先補測試,鎖住現在的行為

重構的定義是「行為不變」,那要怎麼證明行為沒變?答案是在動手之前,先寫好測試。

這種測試的目的不是檢查程式「對不對」,而是記錄程式「現在是什麼樣子」。例如:新增三筆支出後,列出來是什麼順序?某個月的總金額是多少?金額是 0 會怎樣?

請執行 REFACTOR_PLAN.md 的第 1 步:替 storage.py 的公開函式補上測試,
記錄目前的行為。測試要使用 tests/ 裡的範例資料或暫存檔,不可以讀寫真正的 expenses.csv。
先在目前的程式上跑一次,全部通過後告訴我。這一步不要修改 storage.py。

這組測試在舊程式上要全部通過。之後每一步,它都是你的安全網:新的寫法只要讓這組測試繼續通過,就代表行為沒變。

習慣三:小步前進,一步一個循環

接下來照著計畫,一次只做一步。每一步都走同一個循環:

每一步的循環:做計畫裡的這一步、跑測試確認全部通過、用 git diff 讀一遍修改、commit 留下存檔點,然後進入下一步;測試沒通過時修一次,同一步卡住兩次就退回上一個存檔點,把步驟拆得更小再試

步驟小,每次要讀的修改就少,你才真的有辦法檢查。

交代每一步時,說清楚範圍和停下來的時機:

請執行 REFACTOR_PLAN.md 的第 3 步,只做這一步。
完成後執行 pytest,全部通過就停下來,簡短說明你改了哪些檔案,等我檢查。

你檢查時,用 git diff 看修改內容,或請 Agent 用白話說明每個檔案改了什麼。確認沒問題,就留下存檔點:

git add .
git commit -m "重構步驟 3:新增 sqlite_storage.py,通過同一組測試"

習慣四:存檔點與退路

每一步一個 commit,你就有了一連串的存檔點。萬一某一步弄得一團亂,可以直接放棄這一步還沒 commit 的修改,回到上一個存檔點重來。

Claude Code 自己也會記錄對話中的存檔點:輸入 /rewind(或連按兩次 Esc),可以把對話和檔案一起退回先前的某個時間點。這在「Agent 剛剛走錯方向」時很方便。但它是 Claude Code 的功能,Git commit 才是你真正可靠的存檔,兩個都用最安心。

另外兩個退路:

  • 整個重構在一個分支或 worktree 裡做。上一堂學的工具在這裡正好派上用場:主線維持可用,重構完成、CI 通過後再合併。
  • 設一條停損規則:同一步失敗兩次,就不要再叫 Agent「再試一次」。退回上一個存檔點,想想這一步是不是太大,把它拆成兩步再試。一直硬修,常常越修越亂。

什麼時候可以讓多個 Agent 一起重構?

第一堂說過,會改到同一個檔案的工作不適合平行。重構大多是這種工作,所以地基的部分要一個 Agent 依序做完。

但地基打好之後,常會出現一種適合平行的工作:大量彼此獨立、機械式的修改。例如新的函式已經寫好、測試也通過了,剩下的是把三十個不同檔案裡的舊呼叫,逐一換成新的呼叫。這時可以請 Claude Code 派出多個子代理,各自在獨立的 worktree 裡處理一批檔案,最後再一個一個合併、跑測試。

判斷方法還是第一堂那張表:彼此獨立嗎?碰的檔案重疊嗎?結果容易檢查嗎?

行動練習

找一個你想整理的專案(記帳程式、課程裡做過的任何小專案都可以),選一個小型重構,例如「把重複的程式整理成一個函式」或「把一個太大的檔案拆成兩個」:

  1. 開一個新分支或 worktree,用計畫模式請 Agent 寫出 REFACTOR_PLAN.md,包含步驟和「這次不做的事」。
  2. 執行第 1 步:補上鎖住目前行為的測試,確認在舊程式上全部通過。
  3. 照計畫一步一步做,每一步都跑測試、讀 git diff、commit。完成後用 git log --oneline 看看你留下的存檔點。
  4. 故意在某一步請 Agent 做一個你不想要的修改,練習用 /rewind 或 Git 退回上一個存檔點。
  5. 重構完成後推上 GitHub 開 Pull Request,讓上一堂設定的 CI 幫你確認一次。

讀完了嗎?

標記完成後,單元與專案的進度會自動更新(只存在這台裝置的瀏覽器)。

卡住了?讓 AI 當你的家教複製這段提問範本,貼到 Claude 或其他 AI 工具,再補上你卡住的地方。

留言與討論

卡關了、有心得,或發現內容需要更新?都歡迎留言。留言使用 GitHub 帳號登入。

留言區即將開放。現在可以先到 GitHub 討論區 發問,或看看 交流頁 的其他管道。