"Refactoring"
- 7.1 把 handler 邏輯拆成可測單元
分離 HTTP 協定處理與核心邏輯
- 7.2 用 interface 隔離外部依賴
建立小而穩定的測試替身
- 7.3 事件去重邏輯的重構策略
保留語義鍵並降低重複流程
- 7.4 狀態管理的安全邊界
用 lock、copy 與 API 限制保護共享狀態
- 7.5 以 domain 重新整理 package
讓 account、job、event、workflow 這類領域邊界在目錄中可見
- 7.6 逐步遷移到 ports/adapters 架構
用 ports 與 adapters 控制 Go 服務的依賴方向
- 測試的價值發生在它變紅的那一刻——建立、變更與註解分工的防護視角
為一段邏輯決定第一條測試該測什麼、變更或重構前評估既有測試會擋什麼、review 裡爭論某個約束該寫註解還是測試、或拿到紅燈要判讀它是刻意規則還是漏網 bug 時使用。這些問題共用同一個視角:測試的價值在未來的紅燈、不在當下的綠燈。
- 7.7 composition root 與依賴組裝
把具體 adapter、config 與 usecase wiring 留在應用入口層
- 7.8 壓力出現後的重構路線
當 Go 服務變大時,如何按壓力逐步重構邊界
- Characterization Test
斷言「行為不變」而非「行為正確」的測試形態;與正確性測試的語意分界,決定紅燈能不能歸因
- 改既有的程式
要動一段自己沒寫的、或沒有測試保護的程式碼時的選讀
- 重構的動機與策略
從 Hook 系統重構經驗出發,學習何時重構、何時不該重構,以及如何將大規模重構拆分成可管理的階段
- 程式碼壞味道偵測
從三級分類系統到偵測工具鏈,建立系統化的程式碼品質防線
- DRY 原則與共用程式庫
學習識別重複程式碼並建立共用模組,含模組演進與漸進遷移策略
- 配置分離與常數管理
學習消除三種硬編碼問題:魔法數字、配置混合、散落訊息
- 大規模統一化重構
從 44 種不同實作到統一基礎設施:日誌、訊息、風格的三階段漸進式重構
- 作用域迴歸案例研究
從 IMP-003 事件學習 Python 變數作用域的陷阱
- 重構陷阱與防護
三個真實重構事故的共通模式:部分更新問題與系統性防護方法
- 非程式碼的重構
用 Progressive Disclosure 精簡膨脹的規則文件,文件重構和程式碼重構是同一套思維
- 完整案例回顧
從超過 30 個 Hook 各自為政到系統化品質工程,三個階段的完整重構復盤
- 一行查詢放哪、一個測試留不留:結構改動後的兩次「還需要存在嗎」
修完一個應收金額 bug 收尾時,兩個『還需不需要』的問題浮出來。一段『過濾出已結帳品項』的查詢該掛哪一層——inline 在 widget 是洩漏、開一個 service 是儀式,判準是『查詢誰擁有的資料就掛給誰』,答案是既有的 repository。一個為了鎖住修復而寫的測試該不該留——當修復是靠刪掉舊函式、移除參數達成時,那個 bug 被結構免疫了,測試變贅述;判準是『這測試守的失敗模式,結構是否已經替它擋掉』。
- 1101 行自建測試基礎設施、重構刪掉 82.5% — 過度工程的三種形態
自建測試基礎設施前先問框架的標準做法是什麼:mock 純資料物件、helper 帶並發鎖與記憶體洩漏防護、mock 放進 lib/ 進生產依賴圖,三種形態都在重新發明 Riverpod overrideWith 一行就有的東西。精緻的設計文件不是價值證明——它可以精心規劃一個不需要存在的系統。
- 88 行拆成 13 個函式、90 行決定不拆 — 函式長度是症狀、職責才是診斷
同一個團隊、同一條 5-10 行規範、兩個 90 行上下的函式,一個拆一個保留、兩個決定都對。判準在行數之外:職責混雜(初始化/迴圈/儲存/統計擠一起、巢狀五層)拆之,完整業務流程(步驟多但答案只有一個)留之。含 _ImportProgress 收斂參數列與測試耦合行為的守護。
- Domain 層的 947 處硬編碼中文 — 訊息代碼跟顯示文字的分層責任
domain 層的 enum 或 getter 直接回傳 UI 顯示字串時,多語言支援與分層原則同時失守。修法是 domain 只回訊息代碼與結構化資料、UI 層用 translator extension 翻譯;遷移排序從最小模組先行驗證模式。適用於盤點「這個字串屬於領域事實還是呈現」的情境。
- Value Object 的封裝擺盪:從全移除、完全封裝、到加回 .value getter
VO 的封裝邊界在兩個極端之間來回——純字串(零封裝)跟完全封裝(禁止取原始值)各有成立的理由、也各自撞牆。穩態是給原始值一個有語意的官方出口,而不是把「取原始值」本身當違規。含 176 個編譯錯誤的工作量低估、以及「相容性介面」作為理想撤退訊號的判讀。
- 同一個子系統膨脹兩次:異步查詢系統的過度設計震盪
過度設計會復發、且兩輪的機制不同:設計期的膨脹來自想像的需求(別層已處理的重試、用不到的優先級佇列),迭代期的膨脹來自不刪的舊版本(三個實作並存、狀態多處追蹤)。偽需求的檢驗法是問「這個能力已經有別層在做嗎」。
- 兩個 ImportResult 各自都合理 — 傘狀名的碰撞與做一半的重命名
同一個 domain 裡兩個類別都叫 ImportResult:一個是驗證結果、一個是操作結果,各自誕生時都合理。修法是依語意責任命名(ImportValidationResult);而重命名是一組原子操作——類別名、檔名、import、文件宣稱,做一半留下的不一致比不做更迷惑,且宣稱完成與實際完成的漂移要靠稽核抓。
- 取個原始值有四種寫法 — VO 的 toString 洩漏與 accessor 不一致
value object 家族的取值 accessor 各自為政(displayValue、toString、裸欄位)時,殺傷力會在測試層爆開:自定義 toString 讓裸字串斷言全數過期、四種取法讓修復者自己都寫錯。止血是 helper 函數庫集中取值知識、根治是家族統一 accessor 命名。
- 金額型別的三段遷移:double、Decimal、再到 Money extension type
金額欄位從 double 換 Decimal 只解決精度、沒解決「任何人都能對它做無意義運算」;用 Dart extension type 包成 Money 之後,型別系統只開放領域有意義的運算。含 implements Object 的 subtype 設計、以及大規模型別遷移前先寫 characterization test 鎖行為的做法。
- 宣告回歸原生 Exception、三個版本後階層重生 — 需求不死、只是換宿主
「砍掉過度設計、回歸原生」的重構把實作砍了、沒盤點實作回應的需求:錯誤要序列化、要使用者訊息、要分類路由,這些消費者還在,於是基類與階層在新架構裡原地長回來。砍之前分「有消費者的機能」與「沒有的」,口號式目標不可驗收、需求清單可以。
- 核心 entity 重寫、140+ 檔消費端不動 — Deprecated Getter Facade 的過渡設計
重寫被百餘檔引用的核心 entity 時,直接改會同時打爆全部消費端、長期分支的 merge 成本隨時間暴漲。第三條路是 facade:舊欄位保留為 deprecated getter、內部從新結構回讀,消費端零修改編譯通過、@Deprecated 讓編譯器自動列出遷移清單、再逐波清償。facade 要配退場計畫、否則就是永久相容層。
- 溢出 714px、22 個測試同時紅 — 單點修復與規範化的分界
Row 裡未受 Flexible 包裹的固定尺寸元件在窄約束下溢出;同型失敗一次爆 22 個時,正確的產出不是 22 個修復、是一份 overflow 預防規範(反模式清單 + 決策樹 + 測試檢查項)。含測試環境小尺寸是 feature、以及 stale ticket 先考古再執行的教訓。