iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0

前言:「這些規則我早就跟團隊講過了,為什麼還是沒用?」

「依賴要往內指,不能反向依賴」「不要為了假設性的需求先建抽象層」——這些話,只要當過幾年 tech lead,大概都講過不只一次。問題是,講過的規則,在下一次 code review 之前,很容易就被忘記,尤其當生成程式碼的是一個幾秒鐘就能寫出幾百行的 AI,人審查的節奏根本來不及在每一次產出後,重新覆誦一次這些口頭規則。

今天要處理的問題很具體:能不能把「不要過度設計」這句話,寫成一段程式碼,讓它在 CI 裡自動跑、自動擋,而不是只存在於 code review 時某個人的記憶裡?

今日目標

  • 理解「架構測試」(Architecture Test)跟一般單元測試/驗收測試的差異:它驗證的是程式碼的結構關係,不是行為
  • 認識架構測試可以表達哪一類規則:依賴方向、層數限制、禁止特定 pattern 出現
  • 看懂這系列案例裡「多餘的裝飾器鏈」「三層重複的業務規則檢查」這類問題,本質上都是可以被架構測試攔下來的結構性問題
  • 理解架構測試在 Review 分工模型裡扮演的角色:把人的判斷,轉譯成工具能重複執行的規則
  • 建立一個務實的認知:架構測試工具的具體 API 因語言/框架而異,但這個概念是語言無關的

架構測試是什麼:驗證結構,不驗證行為

一般測試(單元測試、驗收測試)問的問題是「輸入 X 會不會得到輸出 Y」。架構測試問的是完全不同維度的問題:「這個類別有沒有依賴到它不該依賴的東西」「這個資料夾底下的類別,有沒有超過我允許的抽象層數」「有沒有出現我明確禁止的 pattern(例如某種被團隊判定為過度設計高風險的裝飾器鏈)」。

這類工具在不同語言生態都有對應的實作,例如 Java 生態常見的 ArchUnit,PHP 生態也有像 phpunit-architecture-test 這類概念相近的工具——但我要在這裡先坦白一件事:這篇文章不打算示範任何一個工具的具體語法,因為我沒有把握能精準複述每個工具版本之間的 API 細節,與其寫出可能過時或錯誤的程式碼騙你「照抄就對了」,不如把這件事講在概念層次:架構測試工具的共同能力,是讓你用程式碼表達「這個模組不能依賴那個模組」「這一層不能超過幾層」這類結構性規則,然後像跑單元測試一樣把它放進 CI,違反規則就讓 build 失敗。

為什麼這件事重要:把記憶轉譯成可重複執行的規則

回頭看這個系列案例裡過度設計版本的兩個具體問題:

  • SqliteUnitOfWork 的 begin()/commit()/rollback() 全部是 no-op,名字承諾了交易保護,實際上什麼都沒做
  • RetryingArticleRepositoryDecorator 宣告 MAX_ATTEMPTS = 3,但邏輯裡沒有迴圈、沒有真的重試

這兩個問題,靠人眼 review 是抓得到的——只要你剛好打開那個檔案、剛好仔細讀了實作內容。但在 745 個檔案的規模下,「剛好打開那個檔案」這件事本身就是碰運氣。架構測試沒辦法直接檢查「這個方法的實作是不是名不符實」(這仍然需要人的判斷),但它可以檢查更前置的結構性問題:例如「裝飾器類別數量超過某個上限就擋下來」「某個目錄底下不能出現超過 N 層的介面繼承」——把「這個系統的抽象層是不是已經失控」這個問題,變成一條 CI 規則,而不是留給 reviewer 憑印象判斷「感覺有點多」。

這正是這個系列主題句要落地的地方:AI 沒有發明過度設計,它只是讓過度設計的速度追上了你按下 Enter 的速度;Review 要跟得上,審的就不能再是程式碼本身,而是產生程式碼的規則。 架構測試就是把這句話裡「規則」兩個字,變成一段真的會執行的程式碼。

常見誤區:把架構測試當成萬能規則引擎

架構測試容易被誤用的地方,是有人會期待它能檢查「這個設計合不合理」這種需要人類判斷力的問題。它做不到,也不該被要求做到。它能做的,是結構性、可窮舉的規則——依賴方向、層數、命名慣例、禁止的 pattern;做不到的,是語意層次的判斷——例如「這個 UnitOfWork 有沒有真的做到交易保護」,這仍然需要人讀程式碼、或搭配整合測試去驗證行為。

❌ 常見誤解:以為架構測試能取代所有人工判斷

「我們裝了架構測試,以後就不用 review 設計了」

✅ 正確定位:架構測試負責結構性規則,人負責語意判斷,兩者互補

架構測試擋下:
- Domain 層依賴到 Infrastructure 層的具體實作
- 裝飾器鏈超過團隊約定的層數上限
- 出現團隊明確列為「過度設計高風險」的 pattern(例如未使用的 CQRS Bus)

人工判斷仍然負責:
- 這個抽象層存在的理由是否成立
- 這個實作有沒有名不符實(像 no-op 的 UnitOfWork)

語言無關的原則,語言相關的工具

這篇要講清楚的分層是:「用可執行的規則取代口頭約定」這件事是語言無關的原則,換成 Java、TypeScript、Python 一樣適用;但具體工具(ArchUnit、phpunit-architecture-test 或其他生態的等效工具)是各自語言生態的實現方式,工具選擇跟語法細節不是這篇文章的重點,重點是「你的團隊有沒有至少一條這樣的規則存在」。

今日思考題

如果你現在打開自己專案的 CI 設定,有沒有任何一條規則是在檢查「依賴方向」或「抽象層數」,而不是只有單元測試跟 lint?如果沒有,你覺得最值得優先寫成架構測試的第一條規則會是什麼?

今日重點回顧

  • 架構測試驗證的是程式碼的結構關係(依賴方向、層數、pattern),不是行為結果
  • 它能把「感覺有點多層」這種模糊印象,轉譯成 CI 會自動擋下的具體規則
  • 它不能取代人類對「這個實作是否名不符實」這類語意問題的判斷,兩者是互補分工,不是取代關係
  • 「用可執行規則取代口頭約定」是語言無關的原則,具體工具是各生態各自的實現方式

明日預告

Day 20 會給出一個更具體、更好量化的規則:複雜度預算——新增一個欄位,改動檔案數到底該有多少上限,用這個系列案例裡真實量測過的數字當基準。

老派工程師的心得

我以前對「架構測試」這種東西是有點抗拒的,總覺得「這不就是又多一種要維護的測試」,多一份負擔。這幾年看著 AI 產出程式碼的速度越來越快之後,我的想法徹底改變了——口頭講過的規則,人會忘,AI 更不會主動遵守,只有寫成會自動執行的檢查,才是真的存在的規則。這聽起來有點無情,好像在說「不能相信任何人(或任何 AI)會自己記得」,但老實說,這幾年帶團隊的經驗告訴我,這句話大部分時候是對的,不是悲觀,是務實。


上一篇
Day 18:GOOS 的核心洞察——讓外層測試逼出你真正需要的類別
系列文
當 AI 寫得比你讀得快:Code Review 該審什麼 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言