一分鐘重點:AI 重構程式碼快到不可思議,一個三百行的義大利麵函式,幾秒鐘就能拆成一疊乾淨的小函式、命名還幫你改好。但重構的本質是「改結構、不改行為」,而 AI 最愛在拆解時「順手」把行為也改了。所以會用 AI 重構的人,靠的不是 Prompt 多神,而是一套紀律:先用測試把現有行為鎖住,一輪只做一種重構,每一步都跑測試驗證,小步提交留退路。把這套流程走順,AI 就是你的重構加速器;跳過任何一步,它就是幫你埋 bug 的高效率機器。
重構是所有工程師都逃不掉、卻又最容易做壞的工作。程式碼會爛,不是因為工程師懶,而是需求一直變,時間一直趕,昨天的正確設計今天就變成技術債。以前重構得手動一行一行搬,慢到大家寧可繞道也不敢動;現在有了 AI,搬程式碼的成本趨近於零,反而讓「該不該重構」變成純粹的紀律問題。這篇要講的就是那套紀律。
先搞清楚:重構不是改寫
這是最重要的一句話,先講在前面。重構的定義是「在不改變外部可觀察行為的前提下,調整程式碼的內部結構」。關鍵字是「不改變行為」。輸入什麼、輸出什麼、有什麼副作用、丟什麼例外,重構前後必須一模一樣。改的只有「人怎麼讀這段程式碼」。
為什麼要死守這條界線?因為一旦你允許「重構順便改一點邏輯」,你就同時在動結構和行為兩件事。哪天測試紅了、線上出事了,你會分不清到底是重構搬壞的,還是那「順便改的一點」造成的。把重構和改功能徹底分開,是資深工程師和菜鳥最明顯的差別之一。
AI 偏偏不懂這條界線。對它來說,「這段程式碼可以更好」是個模糊的目標,它會一邊拆函式一邊覺得「這個 null 判斷好醜,改成丟例外吧」「這兩個分支邏輯重複,合併掉吧」。這些在它看來都是改善,對你的系統卻是實實在在的行為改變。所以整套流程的設計,都是為了框住 AI,讓它「只准改結構」。
為什麼一定要先有測試
想像你要把家裡的承重牆打掉重砌。你會怎麼確認砌完房子沒垮?你需要一個「基準」——砌之前先量好樓板高度、門窗有沒有歪。重構的那個基準,就是測試。
沒有測試的重構,等於閉著眼睛走鋼索。 你改完程式碼,唯一能確認「行為沒變」的方法就是跑測試。如果沒測試,你只能靠肉眼看、靠手動點幾下,這在小改動還行,一旦 AI 幫你動了幾十處,肉眼根本兜不回來。
那老程式碼本來就沒測試怎麼辦?這是台灣很多團隊的真實處境。答案是先補「特徵測試」(characterization test,也有人叫 golden master test)。它的精神很特別:不管現在的行為對不對,先把它原封不動地鎖起來當基準。做法是餵一堆輸入給現有函式,把它現在吐出來的東西記錄成期望值。就算裡面藏著 bug,也先保留——因為重構的目標是「不改變行為」,包括不改變它原本的錯。等重構完、結構乾淨了,再另外開一個 commit 專門修那個 bug,這樣結構調整和行為修正就分得清清楚楚。
這件事正好可以請 AI 代勞。把函式貼給它,說「依現有行為幫我補一批特徵測試,涵蓋正常、邊界、例外三類輸入,斷言要驗到具體的值」,幾分鐘就有一張安全網。細節可以參考我們另一篇〈用 AI 寫單元測試〉。
完整流程:五步安全重構法
把上面的原則收攏成一套可以照做的流程。
第一步,罩測試。 動手前先確認這段程式碼有測試守著。有就跑一遍確認全綠,沒有就先補特徵測試。這一步沒過,後面全部免談。
第二步,切小步。 別想著一次改完。把大重構拆成一連串單一動作:這輪只改命名,下輪只把長函式拆開,再下輪才消除重複的程式碼。每一種動作分開做,好處是 review 快、測試好定位、出事好回退。
第三步,給脈絡下指令。 把要重構的函式、呼叫它的地方、相關的型別和業務規則一起貼給 AI。明講「這輪只做 XX 這一種重構,不准改任何外部行為」,並要求它「逐條列出改了哪些地方」。脈絡給得越完整,AI 越不會亂猜。
第四步,跑測試。 AI 改完,第一件事不是欣賞它多漂亮,是立刻跑測試。綠燈才代表行為沒變,往下走;紅燈就退回這一小步,看是 AI 改壞了還是測試該調。因為你這輪只動了一種東西,定位問題很快。
第五步,小步提交。 這輪過了就 commit,訊息寫清楚「重構:把 calculateOrder 拆成三個函式,行為不變」。下次出事,你可以精準 revert 到任何一個綠燈狀態,不必整包重來。這一個個 commit,就是你的存檔點。
四種最常見的重構,怎麼交給 AI
拆長函式
長函式是技術債的頭號來源。一個函式做了五件事、包了三層巢狀 if、中間還夾著一堆暫存變數,讀的人得在腦子裡同時追蹤十幾個狀態。拆解的原則是「一個函式只做一件事」,把每個邏輯段落抽成有意義名字的小函式。
改之前大概長這樣:
function processOrder(order) {
let total = 0;
for (const item of order.items) {
let price = item.price * item.qty;
if (item.qty >= 10) price = price * 0.9;
total += price;
}
if (order.member) total = total * 0.95;
if (total >= 1000) total = total - 100;
// ...再接三十行運費、發票、通知
}
請 AI 拆完,會變成 calculateItemsTotal、applyMemberDiscount、applyThresholdDiscount 這種各司其職的小函式,processOrder 只負責把它們串起來。可讀性天差地遠。但注意:折扣的先後順序、門檻的臨界值(是 >= 1000 還是 > 1000)絕對不能變,這正是你跑測試要盯死的地方。
改命名
data、tmp、flag、res、d2 這種名字,是閱讀時最大的摩擦來源。好的命名能讓一段程式碼不靠註解就讀得懂。AI 很擅長根據變數的用途建議名字,d 改成 daysUntilExpiry、flag 改成 hasUnpaidInvoice,讀起來立刻順。
命名重構相對安全,但有兩個雷:一是改到公開 API 的名字(函式名、export 的東西),會牽動別的檔案,範圍要看清楚;二是 AI 有時會「順手」連字串內容、log 訊息、甚至 API 欄位名一起改,那些改了是會出事的。所以命名這輪也一樣要看 diff、跑測試。
消除重複
同一段邏輯在三個地方複製貼上,是最典型的技術債。哪天規則變了,你得記得三個地方都改,漏一個就是 bug。AI 很會揪出這種重複,把它抽成一個共用函式。
但這裡有個陷阱要小心:看起來一樣的程式碼,不一定是真的重複。兩段程式碼今天長得像,可能只是巧合,它們背後代表的是兩個不同的業務概念,未來會往不同方向演化。硬把它們合併成一個函式,反而製造了錯誤的耦合,將來一邊要改、另一邊不能改時,你會很痛苦。所以 AI 說「這兩段重複」時,你要判斷的是「它們是同一件事,還是剛好長得像」——這需要業務理解,AI 給不了。
改善可讀性
還有一票小重構能大幅降低閱讀成本:把巢狀的 if 用 early return 攤平、把 magic number 換成有名字的常數、把複雜的布林判斷抽成一個名字清楚的函式(if (user.age >= 18 && user.verified && !user.banned) 變成 if (canPlaceOrder(user)))。這些 AI 都做得又快又好,是投報率最高的一類。
台灣實戰案例:一家 SaaS 新創怎麼清掉三年技術債
分享一個具體的例子。台北一家做電商訂閱管理的 SaaS 新創,團隊六個人,產品跑了三年。核心的「續訂扣款」模組是創業第一週趕出來的,之後需求一直加,那個 renewSubscription 函式膨脹到快六百行,包著十幾層 if,沒人敢動——一動就怕線上扣款出錯,那是會直接噴客訴和金流糾紛的。
他們的技術主管阿哲決定用 AI 來清,但沒有一頭栽進去叫 AI「重構這個函式」。他的做法是:
第一週先補測試。因為原本幾乎沒有測試,他請 AI 針對 renewSubscription 補特徵測試,把各種方案、各種折扣、各種扣款失敗情境現有的輸出全部鎖成基準,總共補了七十幾條。這一步花的時間最多,但他知道這是安全網,省不得。
接下來兩週,他嚴格照「一輪一種重構」的節奏走。第一批只改命名,把 d、s2、tmpFlag 這些換成看得懂的名字,跑測試、提交。第二批把長函式拆開,六百行拆成十七個小函式,每個負責一件事——扣款、算折扣、發通知、寫 log 分得清清楚楚。第三批才消除重複,抽出三個共用的計費函式。
過程中 AI 真的踩了兩次雷,都被測試擋下來。一次是 AI 在拆解時,把一個「試用期用戶跳過扣款」的邊界判斷「順手」合併掉了,它覺得那個分支多餘——結果特徵測試立刻紅燈,因為試用戶的期望輸出變了。另一次是 AI 想把一段「重複」的折扣計算抽成共用函式,阿哲一看發現那兩段其實是「首購折扣」和「續訂折扣」兩個不同概念,只是當時算法剛好一樣,未來一定會分家,就沒讓它合。
三週後,那個沒人敢碰的六百行函式,變成一組清爽、有測試守著的模組。更重要的是團隊心態變了——以前改續訂邏輯像拆炸彈,現在有七十幾條測試罩著,工程師敢改了。阿哲後來在內部分享時講了一句話:「AI 不是幫我重構,是幫我把重構的成本壓到低到我終於願意做。真正守住我的,是那套流程和測試。」
可複製的 Prompt 範本
把上面的紀律寫進一段可以直接用的 Prompt。中括號的部分換成你的東西:
你是一位資深工程師,正在協助我安全地重構程式碼。請嚴格遵守以下規則:
【最高原則】
這是一次「重構」,不是「改寫」。你只能調整程式碼的內部結構,
絕對不能改變任何外部可觀察的行為(輸入、輸出、副作用、丟出的例外、
邊界判斷、執行順序都必須完全一致)。
【這一輪的任務】
這次只做這一種重構:[拆長函式 / 改命名 / 消除重複 / 攤平巢狀判斷(四選一)]。
不要夾帶任何我沒要求的其他改動。
【背景脈絡】
- 程式語言與框架:[例如 TypeScript + Node.js]
- 這段程式碼的職責:[它在系統裡負責什麼]
- 需要留意的業務規則:[例如 試用戶不扣款、折扣先算單品再算會員]
- 呼叫它的地方:[貼上呼叫端,或說明有哪些檔案會用到]
【要重構的程式碼】
[貼上完整函式原始碼,連同相關型別]
【輸出要求】
1. 先用條列說明你打算做哪幾處改動、為什麼,等我確認結構沒問題。
2. 我確認後你再輸出重構後的完整程式碼。
3. 逐條列出你改了哪些地方(改了什麼、從什麼變成什麼)。
4. 如果你發現任何看起來「多餘」但你不確定能否刪的程式碼,
標記出來問我,不要自己決定刪掉——那可能是刻意保留的防呆。
5. 不要修改任何字串內容、log 訊息、API 欄位名,除非我明確要求。
這段 Prompt 的重點在「先講計畫、我確認、再動手」這個節奏,以及最後那條「不確定就問,不要自己刪」。它把 AI 從一個亂改的實習生,框成一個會先報告再行動的助手。
常見錯誤,一個都別踩
一次改太多。 這是頭號翻車原因。同時改命名、拆函式、動結構、順手清邏輯,測試一紅你根本不知道是哪裡壞的,只能整包丟掉。永遠一輪一種、跑一次測試、提交一次。慢,但幾乎不用回頭。
沒測試就重構。 前面講很多了,再強調一次:這是不可妥協的底線。沒有測試,你對「行為沒變」的信心全是錯覺。寧可花一天先補特徵測試,也不要裸奔重構。
AI 順手改壞行為。 AI 分不清重構和改寫的界線,會把它覺得「醜」的邊界判斷、例外處理「改善」掉。這正是為什麼你要在 Prompt 裡把「不准改行為」寫死、要求它逐條列改動、每步都跑測試。測試綠燈是你唯一信得過的裁判。
盲信測試綠燈。 反過來也要小心。如果你的測試斷言寫得很鬆(像 expect(result).toBeTruthy()),那就算行為被改壞了,測試照樣綠。綠燈是「必要條件」不是「充分條件」,重要的重構還是要親眼看 diff。
把「看起來像」當成「重複」。 消除重複時最容易犯。兩段程式碼長得像,不代表它們是同一件事。合併不同業務概念會製造錯誤耦合,將來更難改。這需要你的業務判斷,別全交給 AI。
AI 的限制:它不懂你的業務脈絡
講到底,AI 重構有一條天花板:它只看得到程式碼,看不到程式碼背後的歷史。那個看起來很突兀的判斷,可能是三年前某次半夜事故後補的防呆;那個奇怪的執行順序,可能是為了配合某個上游系統的怪脾氣。這些故事不在程式碼裡,AI 無從得知,它只會依「程式碼本身漂不漂亮」來建議,於是可能把你的救命防呆當技術債清掉。
所以凡是 AI 想刪的東西、想合併的邏輯、想「簡化」的判斷,只要它沒有註解、看起來很突兀,你都要多問一句:「這真的沒用嗎,還是有我不知道的原因?」找得到當初寫的人就問一下,問不到就查 git blame 和 commit 訊息。這個判斷,是資深工程師的價值所在,也是 AI 短期內取代不了的部分。
還有一點:重構完,測試綠燈只是第一關。條件允許的話,該做的整合測試、該在 staging 環境跑的驗證、該找 QA 點的關鍵流程,一樣都不能省。AI 讓重構變快了,但「驗證行為真的沒變」這件事的責任,永遠在你身上。
現在就挑一段程式碼開始
不用等到有「重構專案」才動手。打開你最不想碰的那個檔案,找那個大家繞道走的長函式,照這篇的流程試一輪:先確認有沒有測試,沒有就請 AI 補特徵測試;有了安全網,再請 AI 只做「拆長函式」這一種重構;改完跑測試、看 diff、小步提交。走完一輪你就會發現,那個曾經像拆炸彈的動作,其實沒那麼可怕——可怕的從來不是重構,是沒有安全網的重構。
把這套流程練成肌肉記憶,AI 就是你清技術債的最強幫手。想再把安全網做紮實,接著讀〈用 AI 寫單元測試〉;想把整套 AI 寫程式的手感練起來,〈寫程式的 Prompt 技巧〉和〈Vibe Coding 入門〉都在等你。
本文由 AgentAI 智庫(agentai.tw) 整理製作,台灣最大的繁體中文 AI 知識站。
常見問題 FAQ
用 AI 重構跟自己手動重構,最大的差別在哪?
重構前一定要先有測試嗎?沒測試的舊程式碼怎麼辦?
為什麼 AI 重構時會『順手』改壞行為?
一次請 AI 重構整個檔案可以嗎?
AI 不懂我的業務邏輯,重構會不會出問題?
怎麼判斷 AI 重構完的程式碼可以合併了?
延伸閱讀
每週把這類實戰教學寄給你
訂閱 AgentAI 智庫情報週報,新的 Prompt、AI Skills、工作流與教學第一時間收到。
免費 · 隨時取消