模組三|遷移執行法與驗收(Day 12–16)
重構最貴的一段,不是寫新系統,是新舊兩套同時活著的那段日子。我們的共存期超過一年。
而共存期間有一個月,我攤開開發量發現:舊系統的改動比新系統還多。
**這不是失控,是那段時間的必然。**今天講為什麼。
我們的使用者是外部單位,不是內部同事。
這個差別很大。內部系統你可以發個公告說「下週一換新版」,大家罵歸罵還是得用。但外部單位有自己的作業節奏:他們有月結、有旺季、有自己的教育訓練排程。
你不能挑一個週二早上通知所有人「明天換介面」。
所以我們的做法是分批:先請幾個願意當白老鼠的單位過來,確認沒問題後再一批一批請。而每一批之間要留緩衝,因為前一批的問題要先修完。
這樣做的代價很直接:在最後一個單位切換完成之前,舊系統一天都不能關。
回頭看,這種切法能成立是因為滿足了三件事,缺一個都會很痛:
一、兩套系統指向同一份資料。 使用者今天在新系統改了設定,明天回舊系統要看得到。如果後端是兩套資料,切換就不是「換介面」,是「搬家」,難度差一個數量級。
二、切換是可逆的。 前幾批一定會遇到問題,而你需要能說「沒關係你先回舊的用,我們修好再請你過來」。如果沒有回頭路,第一批出問題就會讓後面所有人拒絕切換,消息傳得比你修 bug 快。
三、有人負責追進度。 「還有誰沒切」這件事必須有人盯,而且要有名單。否則會出現一種很尷尬的狀況:沒有人知道還剩幾個使用者在舊系統上,所以沒有人敢按關機。
第三點聽起來最不技術,但它是整個共存期唯一會「自己拖下去」的環節。
這是我原本以為,後來發現完全不是的部分。
我原本的想像是:新系統上線後,舊系統就進入唯讀維護,只修 P0。實際情況完全不是這樣。
我把四個 repo(新舊各兩套)的月度開發量並排看,發現共存期間有一段時間,舊系統的開發量甚至超過同期的新系統。
原因很簡單,而且事後看很明顯:還沒切換過去的使用者,他們的需求還是要做。
業務不會因為「我們正在重構」就停止提需求。而那些需求提出來的時候,提出者用的還是舊系統。

這帶出共存期最實際的成本:
業務提了一個新報表。
舊系統要做,因為八成的使用者還在那邊。
新系統也要做,因為如果新系統缺功能,沒有人會想切過去。
新功能缺一項,你的切換計畫就多卡一個月。 所以共存期的正確策略是兩邊都做,而且要同時上線,不是「新功能只做新系統」。
這件事有一個很硬的證據,我是翻版本紀錄時才注意到的。
兩個新專案剛開始各自發版,版號和日期都不一樣,這很正常,兩個獨立的專案本來就有自己的節奏。
但從某個時間點之後,它們完全同步了。 同一個版號、同一天發布,一路持續到共存期結束,中間沒有一次例外。
這代表什麼?代表兩套系統被綁在一起出貨了。
而綁在一起的原因,正是前面那條「新功能一定要兩邊都有」:當你要求功能對齊,你就不可能讓其中一邊先發。發版節奏會自動同步,因為它們已經不是兩個專案,是同一個交付的兩個面。
我覺得這個細節很有意思:組織的協作方式,會誠實地印在版本紀錄上。 你不用問任何人,光看發版時間就能推出「這兩個東西是不是被當成一件事在管」。
這些規則沒有人一開始就寫下來,是撞了幾次之後才清楚的:
一、已切換的域:新系統為主,舊系統只收 P0。
使用者都過來了,舊系統那份程式碼的剩餘壽命是有限的。
二、未切換的域:舊系統照常開發,但新需求要順手記下來。
因為這個需求等一下搬到新系統的時候還要再做一次,記下來可以少一次考古。
三、跨域的共用改動:先在新系統做對的版本,再回頭 patch 舊系統。
順序很重要。舊系統的改動最終會被丟掉,投資在那裡的心力回收率是零。先在新系統想清楚,舊系統那份就只是「翻譯」。
四、新功能一定要兩邊都有。
違反這條,切換計畫就會停擺。
前面講的都是前端自己能決定的事。這一段講不能決定的。
舊系統為了同時對接兩套後端服務,在建置指令裡放了一個參數。加了這個參數,程式碼裡好幾處會用三元運算切換服務名稱與 API 路徑:
// 大致的形狀
const serviceName = USE_NEW_BACKEND ? 'service-b' : 'service-a'
const url = USE_NEW_BACKEND ? '/admin/order/list' : '/order/list'
這是後端也在遷移、但還沒收斂完成留下的痕跡。前端只能接住它。
我原本以為重構會順便把這種東西清掉。沒有。
新系統的代理設定裡,至今仍然掛著四個不同的 API 前綴。因為後端到現在也還沒收斂完。
這件事給我的教訓是:
技術債會沿著介面往下游流。前端重構解決不了後端沒收斂的問題,只能先接住它、並且標記清楚。
「標記清楚」是這裡唯一能做的事,至少讓下一個人知道,那四個前綴不是前端的設計,是接住的東西。

整場重構真正的終點,不是「新系統做完」那天。
我去看兩個舊系統的最後一次改動,發現一個違反直覺的形狀。
在關機前的最後兩三個月,舊系統的開發量不但沒有下降,還維持在相當高的水位。
為什麼?因為那是切換前的最後功能對齊:把所有「新系統有、舊系統沒有」和「兩邊行為不一致」的地方一次補平。你不能讓最後一批使用者切過去之後才發現少東西。
然後才是斷崖。其中一套系統在最後一個月的開發量,直接掉到前一個月的十分之一以下,接著就沒有了。
那個斷崖,比任何上線公告都準確地標示了「這件事結束了」。
那一刻才是重構完成。 而它距離「新系統第一次上線」,隔了超過一年。
所以如果有人問你重構的進度,最誠實的指標不是「新系統做了幾成」,是「舊系統還差什麼才能關掉」。
前者會一直看起來很順利,後者才是真的。
一、團隊產出效率在共存期是明顯下降的。 同一個需求做兩次、bug 修兩次、驗收兩次。如果你的主管拿共存期的產出數字去比較,會得到「重構之後效率變差了」的結論,這個要提前打預防針。
二、兩套系統的行為會慢慢分岔。 就算你要求「新功能兩邊都做」,實作的人不同、時間不同,細節就會不一樣。使用者切換過來之後回報「跟舊的不一樣」,你才會發現。這類問題我們遇到不少,而且很難用測試防,因為沒有人寫過舊系統的行為規格。
三、共存期會比你估的長。 我們估的是幾個月,實際超過一年。原因不是技術,是每一批使用者切換都要等他們自己的時間表。
明天 Day 14 講一件遷移期最爽的事:確認某個模組整批不用搬。我會講三種處置方式,以及「怎麼證明真的沒人用」的三個條件,因為這件事只靠 grep 是不夠的。