我們的專案 git 歷史中有 120 個 revert commit 和 12 個 reapply commit。
這不是什麼值得驕傲的數字。每一個 revert 背後都是一段「改壞了、趕快退回來」的故事。但回頭看,這些 revert 記錄了團隊在 Legacy 系統維護中最真實的掙扎。
這篇挑了五個最有代表性的 revert 故事,分析為什麼會發生、怎麼用 Claude Code 降低 revert 的機率。
故事一:34 秒內 4 次 Revert
時間線
同一天內,一個 issue 經歷了 4 次 merge 和 4 次 revert:
10:23 v1 commit — 修正指標頁面打勾顯示問題
11:00 v1 merge PR
11:39 v2 commit — 「還原排版」
11:42 v2 merge PR
13:34 v3 commit — 修正「持續載入中」問題
13:38 v3 merge PR
13:59 v4 commit — 修復因異動導致的載入問題
14:26 v4 merge PR
15:58:13 ❌ Revert v4
15:58:40 ❌ Revert v3
15:58:43 ❌ Revert v2
15:58:47 ❌ Revert v1
四次 revert 在 34 秒內完成,按照 v4 → v3 → v2 → v1 的反向順序。
為什麼會這樣
v1 的 diff 顯示 817 行新增、793 行刪除——1,610 行的變動量。但問題只是「打勾沒顯示」,不需要動這麼多行。
原因是 v1 混入了大量排版修改(空格、縮排、換行)。這讓 code review 幾乎不可能——真正的邏輯修改淹沒在排版差異中。
v2 的存在就是為了「還原排版,只保留 bug 修復」。但還原不乾淨,引入了新的載入問題(v3 修)。v3 又引入其他問題(v4 修)。最後乾脆全部 revert。
教訓
邏輯修改和排版修改永遠要分開 commit。 混在一起的後果:
- Code review 看不出真正改了什麼
- 出問題時無法只 revert 邏輯,排版改動也被退回
- 「還原排版」本身又可能引入新問題
Claude Code 怎麼幫忙
> 這個 diff 有 1,600 行變動,但實際邏輯修改應該很少。
幫我分離出純邏輯修改和純排版修改。
Claude Code 可以讀完整個 diff,標記哪些是空白/縮排變更、哪些是實際邏輯改動。在提交 PR 之前做這個檢查,就能避免「大 diff 小修改」的陷阱。
故事二:傷口標記的五個版本
時間線
一個「傷口癒合後標記消失」的 bug,修了整整一週、5 個版本:
Day 1 v1 — 修正癒合紀錄的標記查詢邏輯
Day 5 v2 — 修正列印和 WORD 匯出中的標記
Day 6 v3 — WORD 匯出的 GD 圖片有問題,退回無標記版本
❌ Revert v3 — 決定還是要標記
v4 — 修改生圖失敗時的預設圖處理
Day 7 v5 — 終於找到根因:換字體
為什麼修了五次
每一版都只修了「表面症狀」,沒有找到根因:
| 版本 | 修了什麼 | 實際根因 |
|---|---|---|
| v1 | SQL 查詢邏輯 | 不是查詢問題 |
| v2 | 列印 + WORD 匯出 | 不是匯出問題 |
| v3 | 退回無標記版本 | 放棄式修正 |
| v4 | 預設圖片處理 | 接近根因但沒到 |
| v5 | 換字體 | FreeSansBold.ttf 不支援 # 符號的渲染 |
v5 的修改只有一行——把字體從 FreeSansBold.ttf 換成 NotoSansTC-VariableFont_wght.ttf。
教訓
在修 bug 之前,先確認根因。 五個版本的工作量加起來可能有幾十小時,但如果一開始就問對問題:「為什麼 # 符號會變成方框?」,答案就是「字體不支援」。
Claude Code 怎麼幫忙
> GD 渲染的 # 符號顯示為方框,可能的原因有哪些?
Claude Code 會列出:1. 字體不支援該字元 2. 編碼問題 3. GD 版本差異。第一個猜測就是正確答案。
故事三:Revert 再 Reapply——44 分鐘的猶豫
時間線
Day 1 merge PR — 某客製功能(取消表單欄位聯動)
Day 7 ❌ Revert — 退回 merge
(44 分鐘後)
✅ Reapply — 又把 revert 退回(等於恢復原本的 merge)
為什麼會這樣
這是一個客製化功能——只影響特定機構。Revert 的原因可能是:有人在其他機構看到了非預期的行為,以為是這個 PR 造成的。44 分鐘後確認不是這個 PR 的問題,又 reapply。
教訓
Revert 之前先確認因果關係。 不是「這個 PR merge 之後出了問題」就代表「問題是這個 PR 造成的」。
這種「先 revert 再調查」的策略在緊急情況下是合理的,但如果不是緊急情況,先調查再決定是否 revert 會避免很多 git 歷史混亂。
故事四:63 秒的 Revert-of-Revert
時間線
11:12:05 ❌ Revert — 批次 revert 三個 issue 的 cherry-picks
11:13:08 ✅ Revert of Revert — 63 秒後把 revert 退回
有人把三個功能的 cherry-pick 一次 revert 掉了。63 秒後意識到不對,又 revert 了那個 revert。
Git 歷史的樣子
commit abc1234 — Revert "Revert issue-X, issue-Y, issue-Z cherry-picks"
commit def5678 — Revert issue-X, issue-Y, issue-Z cherry-picks
Revert of Revert 是 git 歷史中最令人困惑的模式。三個月後有人看到這段歷史,需要花很長時間才能理解「所以最終結果是什麼?這些功能到底在不在?」
教訓
在做大範圍 revert 之前,花 5 分鐘確認影響範圍。特別是批次 revert 多個 issue 時——你可能只想退回其中一個,但 revert 了全部。
故事五:Revert v2 → Revert v1 → Reapply v1 → 繼續開發 v3-v5
時間線
Day 1 v1 commit + merge
v2 commit + merge(4 分鐘後)
❌ Revert v2(4 小時後)
❌ Revert v1(21 秒後)
v3 commit — 重新實作邏輯
✅ Reapply v1(16 分鐘後)
v3 additional commit
v3 merge
Day 2 v4
Day 5 v5 — 最終修正
分析
21 秒內 revert 了 v2 和 v1(反向順序),然後 16 分鐘後 reapply 了 v1。這表示 v1 的修改是對的,v2 才是問題。但 revert 的順序是 v2 → v1(因為 git revert 要從最新的開始),結果 v1 也被誤殺了。
教訓
如果只需要退回 v2,不要連 v1 一起 revert。 Git 可以選擇性 revert 單一 commit。但在時間壓力下,人傾向「全部退回到安全狀態」,然後再逐一恢復需要的部分。
120 個 Revert 的統計
| 模式 | 次數 | 佔比 |
|---|---|---|
| 單次 revert(修壞了,退回) | ~90 | 75% |
| Revert + Reapply(退回又恢復) | ~12 | 10% |
| 多次 revert 連鎖(同一 issue 2+ 次 revert) | ~18 | 15% |
高 revert 率的 issue
有些 issue 特別「多災多難」:
- 電子給藥功能:6 次 revert(跨多個 release 分支)
- 用藥排序功能:4 次 revert
- 照顧紀錄表 V3:3 次 revert(v1/v2/v3)
- 指標頁面打勾:4 次 revert(本文故事一)
- 除錯相關:4 次連續 revert
用 Claude Code 降低 Revert 率
1. 提交前檢查 diff 範圍
> 這個 PR 的 diff 有多少行?
有沒有混入不相關的排版修改?
故事一的教訓:大 diff 是 revert 的溫床。
2. 修 Bug 前先確認根因
> 這個症狀的可能根因有哪些?按可能性排序。
故事二的教訓:不要修表面症狀,花 10 分鐘讓 Claude Code 分析根因可以省 10 小時。
3. Revert 前先確認影響範圍
> 這個 commit 修改了哪些檔案?影響哪些功能?
如果 revert,哪些已經依賴它的修改也會受影響?
故事四的教訓:批次 revert 前先看清楚會連帶退回什麼。
4. 在隔離環境中驗證
> 幫我在 worktree 中測試這個修改,不影響主分支
Claude Code 的 worktree 功能可以在隔離環境中驗證修改,確認沒問題再 merge。
5. 客製化功能的影響範圍檢查
> 這個修改只影響特定機構的邏輯嗎?
有沒有可能影響到其他機構?
故事三的教訓:客製功能出問題時,先確認是不是這個 PR 造成的。
Revert 不是失敗,但可以更少
Revert 本身不是壞事——它是 git 提供的安全網。比起「改壞了硬撐」,果斷 revert 然後重來才是正確的做法。
但 120 次 revert 說明了一些系統性的問題:
- Code review 不足——大 diff 難 review,容易放過問題
- 根因分析不足——修表面症狀導致反覆修改
- 測試環境不足——只能在 release 分支上驗證
- 時間壓力——每週 release 的節奏下,「先 merge 再說」很常見
Claude Code 能幫上忙的是前兩點:讓 diff 更乾淨、讓根因分析更快。後兩點需要流程上的改變,AI 幫不了。
但即使只解決前兩點,revert 率就能顯著下降。因為大部分 revert 的根因都是「改太多」或「改錯方向」。