軟體系統比一個 Repo 更大
有一次 agent 改完前端,TypeScript 沒有錯,unit test 全過,連畫面操作看起來都正常。
到了整合環境,API 卻直接拒絕請求。
問題不在 agent 的程式碼品質,而在我給它的世界太小。前端 Repo 裡的型別,看起來允許一個欄位不填;後端實際使用的 contract 卻有額外限制。更麻煩的是,Helm 設定又決定了整合環境會走另一條 service path。
Agent 只看前端 Repo,自然會得到一個合理、但不完整的答案。
這次經驗讓我記住一個很重要的區分:
可以修改的範圍,和必須理解的範圍,不是同一件事。
公司系統很少剛好只在一個 Repo 裡
小專案常常可以把 Repo 當成整個世界。公司裡的實際服務通常不是這樣:
flowchart LR
F[前端 Repo] --> B[BFF / API Repo]
B --> S[後端服務]
S --> I[部署與環境設定]
C[共用 contract] --> F
C --> B
Ticket 可能只說「調整登入 refresh flow」,真正需要理解的卻包含瀏覽器端狀態、identity service 的 contract、gateway timeout,以及測試環境的 issuer 設定。
如果 agent 的 context 只等於目前目錄,它會很快把模糊處補成自己認為最合理的解釋。Coding agent 的速度,在這裡反而會放大錯誤:錯的 context boundary,會更快產生一個完成度很高的錯誤實作。
Read many, write one
我後來採用的預設模式是:讓 agent 可以讀多個 Repo,但一次 Run 只指定一個主要修改目標。
flowchart LR
A[Coding agent]
A -->|讀 + 寫| F[前端 Repo]
A -->|只讀| P[Payments Repo]
A -->|只讀| I[Infrastructure Repo]
A -->|只讀| K[系統脈絡文件]
對應的 manifest 可以很單純:
repos:
frontend:
path: ../frontend
primary: true
payments:
path: ../payments
context: true
infrastructure:
path: ../infrastructure
context: true
這個 pattern 的價值不是提供真正的 OS 權限隔離,而是把工程責任講清楚:你可以廣泛調查,但這次預期只在 frontend 產生候選變更。
如果 agent 發現後端 contract 必須先改,它應該把那件事列成 dependency 或下一個 candidate,而不是順手跨進另一個 Repo 一起修改。
看得到,不代表可以動
我以前會擔心:給 agent 多看幾個 Repo,scope 會不會失控?
其實隱藏資訊不會縮小問題,只會讓 agent 在資訊不足時猜測。真正要限制的是 mutation,而不是 understanding。
任務描述可以把兩個邊界分開寫:
目標:修正前端 token refresh 時機。
修改範圍:frontend Repo。
脈絡範圍:frontend、identity service、部署設定與 auth contract。
限制:這次 Run 不修改 server contract;若發現需要修改,請列為 dependency。
這種寫法對人也有幫助。Reviewer 一眼就知道這次候選變更承諾了什麼,也知道哪些跨系統問題刻意留到下一步處理。
有些知識本來就落在 Repo 之間
即使把所有相關 Repo 都提供給 agent,仍有一些資訊找不到自然的歸屬:
- rolling deployment 時誰必須向下相容
- staging 和 production 哪裡不一樣
- 哪個 team 擁有某段 contract
- 為什麼兩個重複 model 暫時不能合併
- 哪一組整合測試才代表完整 user journey
這些不是某個 Repo 的實作細節,而是 Workspace 層級的系統知識。
flowchart TD
W[Workspace context]
W --> M[系統地圖]
W --> O[Ownership]
W --> D[部署順序]
W --> T[跨 Repo 測試方式]
W --> C[相容性限制]
它們不一定需要 knowledge graph。幾份有人維護、可以 review 的 Markdown,往往比藏在某次 agent 對話裡可靠得多。
不假裝有 multi-repo 原子交易
有些 feature 確實需要同時改前端、後端與 deployment Repo。但「同一項需求」不代表所有變更要塞進同一次 Run。
多 Repo 同時修改會碰到實際的整合問題:誰先 merge、哪個版本先上線、部分成功怎麼回復、每個 Repo 由誰 review。這些不會因為 CLI 提供一個漂亮指令就消失。
我比較信任有依賴關係的多個 candidate:
flowchart TD
A[Candidate A<br/>後端 contract 向下相容]
B[Candidate B<br/>前端開始使用新欄位]
C[Candidate C<br/>移除舊 contract]
A -->|先整合與部署| B
B -->|確認採用完成| C
Workspace 保存它們的關係,各 Repo 仍保有自己的 review 與 release 邊界。
我在工作上學到的事
那次前端修改在本機看起來完全正確,是因為我們用 Repo 邊界代替了系統邊界。
現在每次交任務給 agent,我會先問兩個問題:
- 它要理解這項工作,必須看到哪些東西?
- 這次我真正希望它修改哪些東西?
答案不同很正常,而且應該被明確寫進 Workspace。
讓 agent 在系統邊界閱讀,在任務邊界修改。
做到這一步之後,agent 的實作比較不容易只在單一 Repo 裡自洽。可是幾週後回頭看,我又遇到另一個問題:Git 清楚保存了 diff,卻沒有替我保存「當時為什麼決定這樣做」。