模組六|工程文化與收斂(Day 27–30)
Day 1 我用三個數字開場。今天回去看它們兩年之後變成什麼。
其中兩個進步了。第三個幾乎沒有動,而它是我當初講得最用力的那一個。
現在是 2 個。
那個「用迴圈把整包設定掛上去」的做法沒有了,改動 JavaScript 原生陣列的做法也沒有了。11 個減到 2 個,這是明確的進步。
但我要講一件我在 Day 4 和 Day 18 都沒說完的事:那保留的兩個裡,有一個本身就是不該存在的做法。
其中一個是權限判斷函式,用在 169 個檔案,這個我在 Day 18 論證過,我認為是對的。
另一個是一個替列表資料補上索引的工具函式,用在 27 個頁面、65 次。
它在做什麼?替每一列資料生一個帶隨機數的 key,然後用它當表格的識別。
而正確的做法是直接指定資料本身的唯一欄位當識別(表格元件本來就支援),那個正確做法在專案裡用了 33 次。
所以現況是:正確做法 33 次,權宜做法 65 次。權宜的那個還是全域的。
這件事很能代表整個系列的基調:我們知道正確答案,而且正確答案在專案裡也有人用,但權宜的那個用得更多,因為它不需要思考。
(Day 23 講過:越需要思考的規則,落地率越低。)
現在大約 145 次。
減少了超過一半。而且新系統的寫法有型別,改子元件的方法名時,編譯器至少會抗議。
但它沒有消失。Day 7 我列過三種替代方案(把條件變成 props、把狀態上移、用雙向綁定),而那 145 次代表有很多地方我們沒有走替代方案,只是把舊寫法翻譯成新語法。
誠實地說:在交期壓力下,「翻譯」永遠比「重新設計」快。 而重寫一百多頁的過程中,不是每一頁都有餘裕停下來問「為什麼需要這個 ref」。

這是我 Day 1 講得最用力的數字。當時我寫:
這個數字我在任何會議裡都不需要解釋,所有人都懂它的意思:任何人改任何東西,都只能靠肉眼和運氣。
兩年之後,新系統有 2 個測試檔。
而且拆開看更難看:其中一個是測那支翻譯腳本的(400 行),真正測業務工具函式的只有 145 行。
幾百個元件,零個元件測試。
我想過這個問題很久,答案不是「沒時間」,因為兩年裡我們做了很多其他沒有立即產出的事(寫規格文件、寫治理腳本、寫專案守則)。
我認為真正的原因有兩個:
一、遷移期補測試,是在為即將被丟掉的東西寫測試。
搬遷的過程中,一個頁面被重寫、被調整、被重構好幾次。這時候寫的測試會一直壞掉、一直要改。於是很自然地「等穩定了再補」。
而那個「穩定了」的時間點,永遠不會有人宣布。
二、測試沒有人催。
需求有人催、bug 有人催、規格文件有人問。測試沒有。 它是唯一一個「不做也不會有人發現」的工作。
Day 25 講過「不會痛的問題,不會被修」。測試是這句話最大的受害者。
Day 23 講過修復佔了將近一半的改動。而其中最大的一類是「搬遷失真」:舊系統有某個行為,新系統漏了。
如果舊系統有測試,這一整類問題會在搬遷的當下就被抓到,而不是上線後由使用者回報。
所以那個「1 個測試檔」不只是舊系統的問題,它決定了整場重構的成本結構。
三十天下來,我認為最值得帶走的是這十條。每一條後面標了它出現在哪幾天。
一、先量化,再說服。
「我覺得很爛」不是論據,「342 個元件、1 個測試檔」才是。(Day 2)
二、找出正在流血的地方,不是最醜的地方。
最長的檔案不一定要先改,又長又常改的才要。(Day 2)
三、重複的代價不是行數,是「它們正在變得不一樣」。
20 個一字不差的檔案不可怕,可怕的是其中幾個已經開始長歪。(Day 3)
四、重構要能切成「隨時可以停、停了也不虧」的段落。
這不是為了進度好看,是為了低潮不會致命。(Day 5、Day 26)
五、框架升級時,先問「這件事還需要做嗎」,而不是「這在新版怎麼寫」。
那 232 次的相容呼叫,正確答案是刪掉。(Day 8)
六、抽象的採用率,取決於它要求使用者改變多少。
框架給的 9,553 行沒人用,自己寫的 457 行用了 25 個頁面。(Day 20、Day 21)
七、先容忍重複,等到你能把兩邊的差異列成一張表,才有資格抽象。
抽早了,你抽的是自己的猜測。(Day 29)
八、重構期的修復數量不是品質指標,是「你看得多仔細」的指標。
真正該擔心的是數量太少。(Day 23)
九、知道哪些醜東西該留下,才叫品味。
但不改的東西一定要留下標記:「破窗」和「刻意保留的疤」在程式碼上長得一模一樣。(Day 25)
十、文件擋不住下一個人,只有會噴錯的東西才擋得住。
這條我們自己違反了兩次。(Day 15、Day 25、Day 28)

比心法更誠實的,是這份清單。這些是我在前面二十九天裡承諾過、但到現在還沒做的事:
| 承諾 | 現況 |
|---|---|
| 「之後會打開嚴格型別檢查」(Day 10) | 沒有 |
| 「地雷應該要能噴錯,不只寫進文件」(Day 25) | 沒有 |
| 「那五個相似的 hook 該合併」(Day 21、29) | 沒有 |
| 「兩個新專案的共用套件該真正共用」(Day 6、29) | 沒有,各一份且已長歪 |
| 「補測試」(Day 2、23) | 幾乎沒有 |
| 「共用元件的使用率該定期檢查」(Day 21) | 沒有機制 |
六項,全部沒做。
而它們有一個共同特徵:都是「不做也不會怎樣」的事。
Day 10 我寫過一句話,現在看起來像預言:
漸進式導入最大的風險,不是導不完,是「暫時這樣」變成永久。
這份清單就是那句話的證據。
一、在計畫裡給「收尾工作」明確的時間點和負責人。
上面那六項全部沒做,不是因為困難,是因為沒有人的名字寫在上面。「之後再說」等於「沒有人」。
二、把規範寫成會噴錯的東西,而不是文件。
我們有三次機會(命名規範、地雷清單、共用元件的使用率),只做到一次(API 結構的冒煙測試,Day 28)。而那一次成功了,證明這條路是走得通的,只是我們沒有走完。
三、遷移的第一個模組,就把測試一起補上。
不是全部補,是選一個模組當示範。因為「補測試」這件事真正的阻力不是工作量,是沒有人知道這個專案的測試該長什麼樣。有一個範例之後,後面的人才有東西可以照抄。
這三十天,我試著誠實地記錄一場重構,包括它做對的地方,和它到現在還沒做完的地方。
我在 Day 1 說過這是團隊的成果,不是我的。整件事的方向由團隊裡一位資深同事定調,而我是實際動手的成員之一,也是那個一直在追問「為什麼要這樣做」的人。
很多決策當下我只是照著做。是後來回頭整理紀錄、跑指令量數字、把四條曲線並排,才看懂它們為什麼是對的,而其中有幾個,我到現在還是不確定。
如果這個系列有一個核心,我想是這句話:
重構不是一個專案,是團隊願不願意一直做正確的小決定。
而那份「未兌現的清單」提醒我:願意做正確的小決定,跟真的會去做,中間還隔著一件事:有沒有人的名字寫在上面。
謝謝你讀到這裡。