生活分享

Claude Code|接手舊專案與逐步重構

建立基準、小步重構並防止舊功能回歸。接手舊專案時,第一個目標是建立可信的基準,再做小步重構。本篇用待辦網站練習:先找啟動方式與測試,再抽出一個計算統計的純函式,確認原有行為沒有改變。你會學會把既有問題、新增問題與尚未驗證項目分開記錄。

閱讀時間約 5 分鐘

接手舊專案與逐步重構:文件、螢幕與完成記號的幾何插圖
圖片:Mokaair (© Mokaair)
本篇目錄
  1. 先做只讀盤點
  2. 保存可比較的基準
  3. 選一個小而獨立的重構
  4. 用行為測試保護重構
  5. 檢查回歸與資料相容
  6. 遇到既有失敗怎麼交付

接手舊專案時,第一個目標是建立可信的基準,再做小步重構。本篇用待辦網站練習:先找啟動方式與測試,再抽出一個計算統計的純函式,確認原有行為沒有改變。你會學會把既有問題、新增問題與尚未驗證項目分開記錄。

可以使用完整參考版獨立開始,也可使用自己的練習成果。先閱讀與 。這次不升級框架、不重寫全部檔案,也不加入資料庫。

先做只讀盤點

先做只讀盤點 → 保存可比較的基準 → 選一個小而獨立的重構
先做只讀盤點 → 保存可比較的基準 → 選一個小而獨立的重構 · 圖片:Mokaair (© Mokaair)
閱讀完整文字說明

接手舊專案與逐步重構,以流程和文件圖形呈現教學重點。

找到 package.json、README、入口 HTML、資料模型與測試,確認程式如何啟動。查看 Git 狀態,知道哪些變更是原本就存在的;不要在不乾淨目錄直接大規模格式化,否則很難辨認這次真正改了什麼。

Claude Code 對話框:接手前只讀盤點 · text
請先只讀取專案,整理入口、資料流、啟動與測試命令。
查看目前 Git 差異,區分既有修改與未追蹤檔案。
列出你需要確認的假設與最小重構候選,先不要修改。

如果 README 與 package.json 不一致,先以實際腳本和執行結果核對,記錄文件落差。不要因 README 宣稱測試通過,就跳過;也不要因現有測試失敗,就把所有失敗歸咎於接下來的修改。

保存可比較的基準

PowerShell 或 macOS/Linux 終端機:在專案根目錄 · bash
git status --short
npm test

保存測試命令、退出狀態與失敗名稱,再人工走一次新增、切換、篩選與重新整理。若某功能原本就壞了,將它列為已知問題,決定是否納入本次範圍。重構應保持既有外部行為,修 bug 則需要另外說明預期行為變更。

大型舊專案若沒有測試,先補一個能描述目前關鍵行為的測試,再改結構。這種測試不是為了證明舊程式完美,而是建立變更前後可比較的觀察點。選重要路徑,不為每個實作細節建立會阻礙重構的測試。

選一個小而獨立的重構

假設介面需要顯示全部、已完成與未完成數量,可以把計算抽到 model.js 的 countTodos,讓 DOM 層只負責顯示。函式輸入是一個陣列,輸出固定統計物件,不讀取 DOM、不寫 localStorage,也不修改原資料。

加入 model.js:純資料統計函式 · javascript
export function countTodos(items) {
  const completed = items.filter(item => item.completed).length;
  return {
    total: items.length,
    completed,
    active: items.length - completed,
  };
}

先確認原專案是否已有相同功能,避免只是新增另一份重複邏輯。若原本就有計算,讓既有顯示改用新函式,保留相同文字與位置;不要趁機改版介面,否則難以判斷差異來自重構還是新設計。

用行為測試保護重構

寫入 tests/count.test.mjs · javascript
import test from 'node:test';
import assert from 'node:assert/strict';
import { countTodos } from '../model.js';
test('統計正確且不修改原始項目', () => {
  const items = [
    { id: 'a', title: '買牛奶', completed: true },
    { id: 'b', title: '整理桌面', completed: false },
  ];
  const before = structuredClone(items);
  assert.deepEqual(countTodos(items), { total: 2, completed: 1, active: 1 });
  assert.deepEqual(items, before);
  assert.deepEqual(countTodos([]), { total: 0, completed: 0, active: 0 });
});

這個測試關心輸出與資料不變性,不要求使用 filter 或某一種迴圈。因此以後改成另一種實作,只要行為相同仍能通過。若測試只檢查程式文字中出現某個函式名稱,對重構的保護就有限。

請 Claude 先加入測試與函式,通過後再接上原介面。每次只做一個能獨立驗證的改動,保存差異,確認沒有不相關的命名或格式變更。若沒有原本的統計顯示,這應被標成小功能新增,而不是純重構。

檢查回歸與資料相容

執行全部既有測試與新增測試,再重走原先保存的瀏覽器操作。若本機儲存格式沒有改變,重新整理後舊資料應仍可讀;如果你確實改了資料格式,則需要相容讀取、移轉或明確的重置策略,不能讓使用者資料悄悄消失。

比較重構前後的畫面與行為,確認完成狀態、篩選結果、刪除目標都一致。測試只覆蓋模型時,仍需人工或驗證 DOM 接線,不能因純函式測試綠燈就跳過介面。

重構前也要確認哪些行為是刻意設計。例如輸入超過長度限制是拒絕、截斷還是顯示錯誤,不能只憑直覺改成自己偏好的方式。先從既有測試、文件與使用情境找證據,必要時把不確定處列為待確認決策。

如果沒有測試覆蓋某個重要路徑,可以先用固定假資料記錄目前輸出,再建立行為測試。這不是要求把所有舊 bug 永久保留,而是讓後續行為改變有明確理由。確定是缺陷後,再寫出新的預期結果與修正範圍。

抽出函式後,檢查呼叫端是否仍使用相同資料來源。把計算移到 model.js,卻在 app.js 傳入篩選後陣列,可能讓全部數量變成目前畫面數量。函式本身測試通過,整體行為仍可能錯誤,因此要驗證接線與資料流。

需要回復時,以本次小步提交或清楚差異為單位,不重置整個含他人工作的目錄。若你還沒提交,先保存需要保留的修改,再使用能精準還原指定檔案或片段的方式。重構範圍小,回復與重新驗證通常也更容易。

交付時寫明哪些行為刻意保持不變、哪些是另行修正的缺陷,以及每個差異對應的驗證。這樣下一位維護者能理解設計原因,不會把必要相容邏輯當成多餘程式刪掉。

遇到既有失敗怎麼交付

狀態回報方式下一步
修改前就失敗附基準命令與錯誤列為既有問題或另開範圍
修改後新增失敗附最小差異與重現修正本次回歸再交付
因環境無法執行說明缺少依賴或服務保留未驗證,不寫通過
靜態檢查正常說明只讀檢查範圍不等同實際執行結果

如果修改範圍越來越大,停下來重新拆分。可以先提交已完成且驗證的小步驟,再處理下一個問題。不要為了讓重構一次「看起來完整」而把依賴升級、功能變更、格式化與資料移轉混在同一差異中。

完成判準是有基準、改動小、測試對應行為、原功能仍可用,且已知問題沒有被隱藏。小練習是把一段重複計算抽成純函式,保存前後證據與回復方式,再用相同方法處理下一個重構候選。

回總目錄

  • 生活分享

    Claude Code|建立第一個 mod:在 Claude Code 行程內數工具呼叫

    寫一個三檔案的 mod,用驗證器與測試確認它掛上的事件。文件把 mod 定義成多了入口檔的 plugin:入口檔叫 hooks module,Claude Code 在事件發生時呼叫裡面的函式,函式可以觀察、改寫或接手事件。

  • 生活分享

    Claude Code|Git Worktree 平行工作

    隔離多個任務的檔案與分支。Git Worktree 讓同一儲存庫擁有多個工作目錄,各自使用分支與檔案。本篇會把待辦篩選與文件整理分開,確認兩個 session 不會直接改到彼此的檔案,再把其中一個成果整合回主分支。你也會知道何時可以安全清理工作目錄。

  • 生活分享

    Claude Code|雙 Worktree 實作與衝突整合

    隔離兩項功能,最後完成整合與回歸。兩個 Claude 工作階段同時編輯專案,最容易出現的問題是互相改到同一份檔案,或各自測試通過、整合後卻失敗。本篇用兩個 Worktree 分別處理篩選預設值與介面文字,故意製造一次小衝突,再完成整合、驗證與清理。你不需要先啟用 Agent Teams。

  • 生活分享

    Claude Code|比較流程品質、用量與執行時間

    以同一資料集比較兩種工作方法。比較兩種 Claude 工作方法時,不能只挑成功那一次,也不能只看第一個答案有多快。本篇用固定案例、原始紀錄和一致判準,比較品質、重試、等待與人工整合時間,最後寫出有樣本數與限制的報告,而不是保證某個方法一定省錢。

最新旅遊情報攻略

資料來源

生活分享