「昨天講的 PSR-17/18 介面化,聽起來就是把一支函式包一包,套用到整套廠商接線應該也差不多吧?」
如果只有一支函式,確實差不多。但 legacy 系統裡一家外部廠商的接線,很少只有一支函式——認證邏輯、資料查詢邏輯、狀態同步邏輯、回應格式解析,往往散落在好幾支檔案裡,甚至同一段邏輯被複製貼上到不同的呼叫端,各自加了一點微調。這時候問題已經不是「這支函式能不能測試」,而是「AI 根本沒辦法窮舉這家廠商的邏輯到底散落在系統裡多少個地方」。
這正是 Day 01 那句話的另一種樣貌:AI 對著眼前這一處呼叫端說「已經確認過了,改這裡是安全的」,但它沒有辦法確認的是——系統裡還有沒有其他呼叫端,也依賴著同一家廠商、同一套沒被它看到的邏輯。
裸 curl 呼叫(Day 10 講的問題)是「這一段程式碼沒辦法被驗證」,而散落各處的廠商接線是更大一層的問題:這家廠商的邏輯,到底被複製貼上到系統裡多少個地方?各自有沒有悄悄修改過? AI 改掉它找到的那一處,測試綠燈,看起來沒問題——但它沒有辦法保證,還有沒有另一個呼叫端,用著同一家廠商但版本不同步的邏輯。
這不是危言聳聽的假設。廠商的 API 協定會改版、認證方式會調整、回應格式會微調——如果同一家廠商的接線邏輯散落在三、四個地方,每次協定變動,理論上要同步改三、四處,但實務上常常只改了 AI 當下看到的那一處,其他地方繼續用著舊版邏輯,直到某天正式環境噴錯才被發現。
解法是把一家廠商的整套接線邏輯,拆成一個獨立的 package,內部至少切成兩個明確的職責:
用一組對照來看這個差異:
❌ 接線散落各處:
// 呼叫端 A
$response = curl_post($vendorHost . '/order/list', $authHeader, $params);
// 呼叫端 B(另一個功能,複製貼上時微調過參數順序)
$response = curl_post($vendorHost . '/order/list', $params, $authHeader);
→ 同一家廠商,兩處呼叫端的參數順序不一致;
AI 改動任何一處都無法確定另一處是否也需要同步修改,
甚至可能根本不知道還有另一處存在
✅ 收斂進獨立 package:
$client = VendorFactory::client($vendorId);
$orders = $client->listOrders($params);
→ 呼叫端只依賴一個穩定介面,
參數順序、認證方式、URL 組成全部收斂在 package 內部,
改動這家廠商的邏輯只需要改一個地方,
且 package 邊界本身就是 AI 改動範圍的具體邊界
package 邊界的價值,不是「程式碼比較整齊」這種表面理由,而是把「AI 沒辦法窮舉的散落範圍」,變成「AI 只需要在這個資料夾裡找」的具體邊界。 呼叫端還有多少處在用這家廠商,仍然要靠 grep 或 IDE 的引用查詢去確認,但至少「這家廠商的邏輯本身長什麼樣子」不再是一個要在好幾支檔案裡拼湊的問題。
實際遷移時,AI 最容易犯的錯誤是太快動手:看到散落的呼叫端,覺得邏輯不複雜,直接照著自己的理解寫一個新 package。但舊接線裡往往藏著只有踩過坑才會知道的細節——某個欄位在特定情況下要轉型、某個錯誤碼要特殊處理、某個逾時設定是為了因應這家廠商偶爾回應很慢。這些細節可能沒有寫在任何文件裡,只存在於舊程式碼本身。
正確的順序是:先把舊接線的行為,用測試斷言下來(哪怕測試本身很醜、只是把現有行為釘住),再開始寫新 package,最後用新舊行為比對(呼應 Day 06 講的乾淨基準比對)確認新 package 的行為跟舊接線完全一致,才把呼叫端一個個切換過去。先釘住行為、再重構,而不是先重構、再祈禱行為沒變——這條紀律在單一函式的介面化裡已經重要,在整套廠商接線的遷移裡更重要,因為要重建的邏輯範圍更大,AI 憑理解重寫時漏掉細節的機率也更高。
把散落的第三方整合邏輯收斂成一個邊界清楚的模組是語言無關的設計原則——這件事跟 Day 08 講的 Repository 收斂裸寫 SQL 是同一種思路,只是收斂的對象從「資料庫存取」換成「外部廠商整合」。今天用 PHP 的 composer package 當實現方式,換成 Java 可能是獨立的 Maven 模組,換成 Node.js 可能是獨立的 npm 套件或 monorepo 裡的一個 package——工具不同,但「先把散落邏輯圈出一個邊界,讓改動範圍變得可控」這個判斷邏輯是一致的。
回想你手上系統裡跟某個外部服務整合的邏輯:如果要列出「這家服務的邏輯到底散落在系統裡哪些地方」,你有把握一次列完嗎,還是得靠反覆 grep、每次都可能漏掉一兩處?
明天要講收斂進 package 之後、容易被忽略的下一個問題:這個廠商 package 本身,能不能依賴外層專案的 vendor 目錄?答案是不能,而背後的理由,關係到這個 package 未來能不能被安全地獨立測試、獨立升級。