「我們已經把架構邊界講清楚了,AI 也照著寫,為什麼程式碼變得更難懂,不是更好維護?」
這是把架構邊界建立起來之後,很容易碰到的下一個問題。前面幾天講的都是「沒有邊界會怎樣」,但邊界本身也可能被誤用——AI 一旦知道「這裡有個邊界該遵守」,很容易把「遵守邊界」理解成「多包一層」,而不是「把改動範圍收斂到對的地方」。 結果邊界立起來了,程式碼卻多了三層它根本不需要的抽象。
這系列從 Day 03 開始就在講一件事:架構的本質是約束「能改到哪裡」,不是規範「怎麼寫」。 Repository 分層收斂的是資料存取邏輯的改動範圍,介面切分收斂的是「呼叫端跟哪個實作綁死」,模組邊界收斂的是「一個改動能不能被限制在一個資料夾內」——這些機制的共同點是:它們解決的是「AI 下次改這裡時,需要查證的範圍有多大」這個問題。
但 AI 讀到「這裡有架構規則」的時候,容易產生一種過度反應:既然架構重要、邊界重要,那是不是應該在每個接觸點都包一層 interface,每個建立物件的地方都套一個 factory,每次呼叫外部服務都包一個 adapter?這種反應的問題在於,它把「架構規則存在」直接等同於「這裡需要更多間接層」,卻沒有先問一個更基本的問題:這一層間接性,實際上收斂了什麼改動範圍?
❌ 為了「看起來符合架構」包裝三層:
interface NotificationSenderInterface {
public function send(string $to, string $message): void;
}
class NotificationSenderFactory {
public static function create(): NotificationSenderInterface {
return new EmailNotificationSender(new SmtpAdapter(config('mail')));
}
}
class EmailNotificationSender implements NotificationSenderInterface {
public function __construct(private SmtpAdapter $adapter) {}
public function send(string $to, string $message): void {
$this->adapter->deliver($to, $message);
}
}
class SmtpAdapter {
public function __construct(private array $config) {}
public function deliver(string $to, string $message): void {
// 實際寄信邏輯
}
}
→ 四個類別、一個 factory,全專案目前只有一個地方會用到「寄通知信」,
也沒有計畫支援第二種通知管道;這一層層間接性沒有收斂任何實際存在的改動範圍,
只是讓「這裡寄了一封信」這件事,變成要跳四個檔案才看得完的迷宮
✅ 只在真正需要替換點的地方放邊界:
class NotificationSender {
public function send(string $to, string $message): void {
mail($to, 'System Notification', $message);
}
}
→ 一個類別,一個方法,日後如果真的要支援簡訊或第三方通知服務,
再抽出介面也不遲——現在的程式碼直接反映了現在的真實需求
間接層的價值不是「看起來更架構」,是「未來這裡真的會有替換點的時候,替換不用牽動呼叫端」。 如果一個系統目前確實只有一種通知方式、短期內也沒有計畫增加第二種,那一層介面加上一個 factory,不是架構嚴謹,是在還沒有問題的地方預先解一個不存在的問題。
人類工程師在寫程式碼時,通常帶著「這個專案接下來半年會怎麼演進」的直覺——這種直覺來自對業務脈絡、團隊規劃的長期理解。AI 沒有這種脈絡,它看到的只有「這裡有架構規則」跟「眼前這段程式碼」,缺少「這個抽象層在可預見的未來會不會真的被用上」這個判斷依據。
於是 AI 容易退回一個安全但錯誤的策略:看到任何跟「架構」沾得上邊的規則,就往「加更多結構」的方向解讀,因為『加了總比沒加安全』。 但這個判斷邏輯忽略了一件事——多餘的抽象層不是零成本的,它增加了讀者理解程式碼需要跳的檔案數量、增加了未來要維護的介面契約,這些成本是真實存在的,不會因為「看起來比較架構」就消失。
架構邊界要解決的是「真實存在的改動範圍問題」,不是「看起來像架構的形式」——如果一層抽象拿掉了,程式碼行為完全不變、也沒有任何已知的未來需求會用到它提供的彈性,那這層抽象就是在製造間接性,不是在收斂邊界。
面對「這裡該不該加一層介面/抽象」的判斷,一個實用的問題是:如果把這一層拿掉、直接呼叫具體實作,今天、下個月、可預見的未來,會不會有任何一個已知的理由需要替換掉這個具體實作? 如果答案是「不知道,但以防萬一」,那通常代表現在還不需要這層抽象——「以防萬一」不是一個具體的改動範圍問題,是一個假設出來的、還沒發生的未來。
這跟一條常見的重構紀律是同一個判斷邏輯——「沒有第二個消費者就不要先抽共用層」:抽象值不值得建立,看的是「現在有沒有真實存在的第二個場景」,不是「理論上可能會有」。架構邊界也一樣——邊界該立在哪裡,看的是「現在真實存在的改動範圍風險在哪裡」,不是「架構原則上哪裡都可以立一道邊界」。
回想你手上系統裡某個包了 interface + factory 的地方:如果拿掉這層包裝、直接呼叫具體實作,會不會有任何已知的、具體的理由需要替換掉它?如果想不出來,這層抽象可能從一開始就不是為了解決真實問題而存在的。
明天用一個具體案例,把今天講的「過度抽象化」完整走一遍:AI 為了「遵守分層」,把一個一行程式碼就能解決的簡單功能,包成了四層架構。