模組三|遷移執行法與驗收(Day 12–16)
我們團隊修 bug 的時候,commit 訊息會標一個階段標籤:這個問題是在哪一關被抓到的。
六年下來,這變成一份免費的統計資料。我把兩個新專案的標籤數出來:
| 在哪一關被抓到 | 專案 A | 專案 B |
|---|---|---|
| TEST(開發自己的測試站) | 465 | 893 |
| FAT(內部驗收) | 12 | 44 |
| UAT(使用者驗收) | 11 | 11 |
| 正式環境 | 11 | 8 |
兩個專案合起來:1,455 個被記錄的問題,其中 19 個是在正式環境才被發現的(約 1.3%)。
這是一個漏斗,而且它在運作。今天講這個漏斗每一層在擋什麼。
我承認我剛進公司的時候,看到 TEST、FAT、UAT 只覺得是「好幾個測試站」,差別大概是「越後面越接近正式」。
這個理解是錯的。 它們的差別不在「有多接近正式環境」,在誰在測、他關心什麼。
TEST 是廚師自己試味道。 他關心的是:鹹淡對不對、有沒有煮熟、擺盤有沒有歪。他知道這道菜「應該」是什麼樣子。
FAT 是店長試菜。 他關心的是:這道菜符合我們菜單上的描述嗎?成本控制得住嗎?出餐速度可以嗎?他懂餐廳,但他不是客人。
UAT 是找熟客來試吃。 他不管你的成本和動線,他只會說:「這個份量對一個人來說太多了」「醬汁很好吃但麵有點軟」。
三種人挑出的毛病完全不同,而且後面的人挑的毛病,前面的人通常想不到。
這就是為什麼不能跳關。TEST 抓的是「壞掉」,UAT 抓的是「不合用」——而「不合用」是寫不出測試案例的。
有幾種驗收,就要有幾套環境。這件事在新系統是靠設定檔支撐的。
新專案有六到七個環境設定檔(開發、測試、正式、容器化、打包分析、本機覆蓋),而建置指令的差別只剩「模式」:
vite build # 正式
vite build --mode test # 測試
vite build --mode docker # 容器化
對照舊系統:它只有兩個設定檔,而真正的環境差異(後端位址、代理規則)寫在建置設定檔裡,切換靠人工註解(Day 11 講過)。
這個差別在重構期特別關鍵,因為你會需要一件事:讓新舊兩套系統指向同一套後端。
沒有這個能力,你就沒辦法做功能對照,而功能對照是重構驗收的核心,下一段講。
這是我覺得最少人講、但最重要的一段。
一般開發的驗收問:「這個新功能對不對?」
你有規格、有設計稿、有驗收條件。照著檢查就好。
重構的驗收要問:「這個舊功能,有沒有變得不一樣?」
而這個問題沒有測試案例可以照著跑,因為「原本就是這樣」從來沒有人寫下來。
舉個我們實際遇到的類型:
舊系統的某個查詢,日期欄位留空的時候會預設查最近七天。
新系統重寫之後,留空就是不限日期。
兩個行為都很合理。沒有任何文件寫過該是哪一種。而使用者切換過來之後,會覺得「新系統怎麼變慢了」,因為他習慣留空,而現在那會撈出全部資料。
這種問題有三個特徵,每一個都讓它很難被抓到:

所以我們後來的規則是:搬完一個模組,主動跑一次新舊對照,而不是等被回報。
具體做法很土:把舊系統和新系統開在兩個瀏覽器分頁,同一組查詢條件各打一次,比對結果。差異列出來給業務確認:是刻意改的,還是搬漏了。
這件事聽起來很費工,但它把問題攔在 FAT 之前。而攔在 FAT 和攔在正式環境,成本差一個量級。

還有一個重構期的常見狀況:前端做完了,後端還沒好。
這時候你有兩個選擇:等,或是先用假資料做完。
我們的做法是後者——先注入假資料把畫面和流程做完,等後端上線再換掉。
但這裡有個規則很重要:
「移除假資料」這件事,要開一張獨立的票。
如果你只是在程式碼裡留一個 // TODO: 等後端 的註解,那個假資料有很高的機率會活到正式環境。因為:
我們有一個具體的實踐是:假資料要明顯到會被發現(例如金額固定用一個不可能出現的數字)。寧可醜,也不要真到讓人忘記它是假的。
一、關卡越多,交付越慢。 一個小改動要走完 TEST → FAT → UAT,可能是兩週。所以要有分級:不是每個改動都要走完整流程,緊急修復要有快速通道,但快速通道要有紀錄,否則它會變成預設路徑。
二、標籤統計有偏差。 前面那些數字來自 commit 訊息的人工標籤,會有人忘記標、也會有人標錯。趨勢可信,絕對值不要當成品質指標拿去比較。我自己也是把它當「漏斗有沒有在收窄」的參考,而不是 KPI。
三、多環境要有人維護。 六七個設定檔代表六七套可能失同步的東西。我們遇過「測試站好好的、容器化環境壞掉」,原因是某個設定只加在其中一邊。
模組三到這裡結束:怎麼切第一刀、怎麼在共存期活下來、哪些不用搬、什麼時候改名、以及怎麼確保東西不會壞到使用者手上。
明天開始模組四,進到系統內部。Day 17 講路由與選單——我們換了框架,但沒有換心智模型。而重寫之後,程式碼行數變成原本的兩倍以上。