模組五|重構期的品質與節奏(Day 22–26)
我把新專案裡「刪除行數遠大於新增行數」的改動排出來(已濾掉自動產生的檔案):
| 新增 | 刪除 | 內容 |
|---|---|---|
| +846 | −11,885 | 一個設定彈窗的重構 |
| +3,039 | −4,521 | 訂單詳情模組重構 |
| +11 | −1,934 | 多個彈窗開關變數合一 |
| +180 | −1,249 | 彈窗合併進通用元件 |
| +0 | −1,748 | 刪掉框架的範例路由 |
| +0 | −485 | 刪掉框架的範例表單 |
第一名:新增 846 行,刪掉 11,885 行。比例是 1 比 14。
第三名更誇張:新增 11 行,刪掉 1,934 行。
這不是巧合,是一種明確的品味。今天講這種品味長什麼樣。
這是最常見的一種,也是最反直覺的。
一般的認知是「檔案拆得越細越好」。但實際上,如果一個子元件:
那它的存在只帶來一件事:閱讀時要多跳一個檔案。
你在讀一份食譜,讀到第三步的時候寫著「請參閱附錄 B」。
你翻到附錄 B,發現那裡只有一句話:「把火轉小」。
這句話為什麼不直接寫在第三步?
如果附錄 B 被十個步驟引用,那它值得存在。如果只有一個,那它只是讓你多翻了一次書。
判斷標準很簡單:被幾個地方用? 一個的話,就該考慮併回去。

那個「新增 11 行、刪掉 1,934 行」的改動,做的就是這件事。
典型的舊寫法:
// 一個彈窗一個開關
const showEditDialog = ref(false)
const showDetailDialog = ref(false)
const showHistoryDialog = ref(false)
// ...再來十幾個
然後每個地方開關的時候,都要記得把其他的關掉,否則會出現兩個彈窗疊在一起。
於是每個開關函式裡都有一串「把別的設成 false」。十幾個開關,就是十幾組互相關閉的程式碼。
改成單一狀態之後:
const activeDialog = ref<'edit' | 'detail' | 'history' | null>(null)
互斥變成語言本身保證的事,那十幾組互相關閉的程式碼全部消失。
這是我最喜歡的一種刪減:不是把程式碼寫短,是換一個讓錯誤不可能發生的表達方式。
前面那兩筆 +0 的改動,是專案最早期就做的(Day 12 講過)。
框架附的範例路由、範例表單,加起來兩千多行。它們不會造成 bug,但會進搜尋結果、被新人當成慣例照抄。
新增零行、刪除兩千行,這種改動的價值不在當下,在往後每一次搜尋。
講完刪,講不能刪的。
專案裡有這樣的程式碼:
// 暫時關閉拖拽功能,等下一版再上
// dragEnabled: true,
被註解掉,但寫了原因。
這跟「死碼」不一樣。死碼是沒有人知道為什麼還在的程式碼;而這個是有意識的暫緩。
差別在那一行註解。有註解的,下一個人知道能不能刪;沒註解的,下一個人只能留著。
我們的規則因此是:
要留的東西,一定要寫原因;沒有原因的,直接刪。
而且原因要寫「什麼時候可以刪」,不只是「為什麼留」。上面那個「等下一版再上」就有這個性質——它自帶一個到期日。
(誠實補充:這種帶原因的暫留碼在專案裡只有個位數個。大部分被註解掉的程式碼,還是沒有原因的。 規則訂了,執行率不高。)
還有一種刪減比較少見,但我覺得值得單獨講:重構的過程中,順手拿掉不再需要的功能。
翻改動紀錄的時候,我看到好幾筆是這種形狀:
這些不是「換個寫法」,是真的少了一個按鈕。
而它們能發生,是因為重寫的時候你會被迫問一個平常不會問的問題:「這個功能真的有人在用嗎?」
平常沒有人會為了刪一個搜尋條件開一張票,投報率太低。但當你已經在重寫這一頁了,順手拿掉的成本趨近於零。
遷移是產品瘦身成本最低的時刻(Day 14 從模組層級講過這件事,這裡是欄位與按鈕層級的版本)。
離開營地的時候,讓它比你來的時候更乾淨一點。
重點在「一點」。它不是要求你每次去露營都做一次大掃除,是要求你順手。
實務上的形狀是:你為了修一個 bug 打開某個檔案,順手把旁邊那個沒用到的 import 刪掉、把那段註解掉三年的程式碼清掉。
單獨看,每一次都微不足道。 但如果一個團隊十個人、每人每天打開三個檔案,那就是每天三十次微小的清理。
而反過來也成立:如果每個人都覺得「這不是我的責任」,那檔案只會單向地長胖。

我第一次拉「刪除最多」的排行榜時,前四名長這樣:
+9,236 −124,591
+167 −81,453
+0 −81,374
十二萬行!八萬行!我差點就把這個當成頭條寫出來。
然後我去看了那些改動的內容——它們是清理自動產生的快照檔。
那些檔案是某個工具產生的中間產物,本來就不該進版控。有人發現之後,把它們從版控裡移除、改成「用完即丟」。
這是好事,但它不是重構。它是清理垃圾。
如果我把它當成「我們刪了十二萬行程式碼」寫出來,那是在誇大成果。濾掉自動產生的檔案之後,真正的頭條才浮出來——那個 1 比 14。
量化重構成果的時候,第一步是先確認你量的是不是「人寫的程式碼」。
一、刪減型的重構,在數字上看起來像「沒有產出」。 一個週期下來新增行數是負的,如果團隊用產出量衡量績效,這種工作沒有人想做。
二、合併子元件回主檔,會讓單一檔案變長。 那個 1 比 14 的改動之後,主檔確實變大了。這是把「檔案數量」換成「單檔長度」,我認為值得,但它確實違反了「檔案要小」這個常見的直覺。
三、「刪掉沒人用的東西」需要證明沒人用。 這件事的成本在 Day 4 和 Day 14 都講過,這裡再次成立。我們有一批東西是「知道可以刪、但還沒證明」的狀態,它們就一直躺著。
明天 Day 25 講相反的一面:有些醜東西,我們知道它醜,但決定不改。 包括一個拼錯的欄位名,和一個會讓使用者被踢出系統的錯誤碼。