Claude Code 跨多 Repo 開發完整教學 — 用 Virtual Monorepo 串接 35 個服務
想像你接手一個系統,它有 35 個 GitHub repo。
event-ingestion-service、stream-processor、analytics-api、ops-dashboard、streaming-cluster、database-cluster⋯⋯每個都在獨立的 repo 裡,各自有 CI、各自的 PR review、各自的 release 流程。你打開 Claude Code,cd 進其中一個 repo,問它:「使用者按下儀表板的『重新計算』按鈕之後,後台會發生什麼事?」
它只能看到當下這個 repo 的程式碼。後台處理在 stream-processor,重新計算邏輯在 analytics-api,資料來源在 event-ingestion-service——這些 Claude 一個都看不到。它能告訴你的,最多就是「這個按鈕呼叫了 /recompute 端點」,後面發生什麼事就要靠你自己腦補。
這不是 Claude 不夠聰明的問題。是它沒看到那張地圖。
問題不在 repo 結構,在「可視範圍」
很多人聽到「跨 repo 開發」第一反應是:那我們把 35 個 repo 合併成 monorepo 好了。
這想法的問題是它太大。每個 repo 的 CI/CD、release 流程、權限切割都是團隊現有運作的一部分,動結構代價極高。而且就算合併成功,Claude Code 也不會神奇地變強——你還是得告訴它哪段程式碼跟哪段有關係,只是現在它們躺在同一個目錄樹裡。
真正的問題不是「程式碼分散在多個 repo」,是「Claude 沒辦法看到這些 repo 之間的關係」。
換個角度想,就像你在一間有 35 個房間的辦公大樓工作。每個房間裡都有不同的部門。你不需要把所有牆推倒變成開放空間,你只需要一張樓層平面圖,知道採購部在 3 樓、財務在 7 樓、IT 在 B1。
Virtual Monorepo 就是這張平面圖。
三個檔案搞定整張地圖
整個模式只需要三個檔案,放在一個輕量的 workspace 目錄裡:
.repos — 一個 bash script,把所有 repo 用 git clone 拉下來,放到統一的目錄結構。一次設好,新成員入職跑一次就拿到完整環境。
CLAUDE.md — 系統地圖。每個服務有什麼用、跟哪些服務有依賴關係、資料怎麼流動。這份是 Claude 每次啟動 session 都會自動讀的。
README.md — 給人看的高層架構脈絡。新人入職、外部協作者都看這個。
目錄長這樣:
1 | workspace/ |
關鍵在於:workspace/ 本身只是一個容器。底下每個子目錄都還是獨立的 git repo,各自有自己的 .git、自己的 CLAUDE.md、自己的 CI。Workspace 不接管它們,只是提供一個共同的「閱讀視野」。
CLAUDE.md 的層級結構(這是核心)
Claude Code 啟動時,會從你 cwd 開始往上爬目錄樹,把所有遇到的 CLAUDE.md 都讀進來。這個行為是 Virtual Monorepo 模式的關鍵。
意思是當你在 workspace/services/event-ingestion-service/ 裡跑 Claude Code,它會自動把這三個檔案串起來看:
workspace/services/event-ingestion-service/CLAUDE.md(repo 層級——這個服務的具體細節)workspace/services/CLAUDE.md(如果有的話,team 層級——這群服務的共同約定)workspace/CLAUDE.md(org 層級——整個系統的地圖)
所以你的工作其實是設計三層內容,從特定到通用:
Repo 層級——build 怎麼跑、測試怎麼下、有什麼怪 patch 要小心。這層保留在每個 repo 裡,跟著該 repo 的 commit 走。
Team 層級——這群服務共用的規範(例如「我們所有的 data service 都用 Protobuf 不用 JSON」)、跨服務的測試策略。這層放在 workspace 裡用 git 追蹤。
Org 層級——整個系統的服務圖、資料流方向、最重要的:每個服務在什麼路徑、做什麼事。這層也放在 workspace 裡用 git 追蹤。
注意 org 層級要寫得「短」。Claude 每次啟動都會讀,太長就會吃掉你寶貴的 context window。
系統地圖怎麼寫才好用
CLAUDE.md 的 org 層級,內容該長什麼樣?最有效的格式是「服務目錄 + 一行描述 + 依賴指向」。
1 | # 系統地圖 |
@path/reference 這種寫法是給 Claude 看的快捷指路標。當你問「使用者按下重新計算之後會怎樣?」,它一眼就能跟著 @services/analytics-api → @services/stream-processor → @services/event-ingestion-service 把整條鏈追完。
關鍵原則:寫架構關係,不寫程式細節。細節的部分讓 Claude 自己去看程式碼,地圖只負責告訴它「該去看哪些檔案」。
設定流程:從零到能用
實際操作只需要四步。
第一步:建 workspace 目錄並 init 一個 bootstrap repo
1 | mkdir my-system-workspace && cd my-system-workspace |
.gitignore 那行是關鍵——讓 git 忽略所有子目錄裡的 repo,但保留每個子目錄裡的 CLAUDE.md。這樣 team 層級的文件能跟著 workspace 一起進版控。
第二步:寫 .repos 腳本
1 |
|
第一次跑這個腳本花幾分鐘,之後新成員或新機器只要 ./.repos 一次就拿到完整環境。
第三步:寫 CLAUDE.md(org 層級)
照前面講的格式,列出每個服務的用途和依賴。先寫個 v1,能跑就好,後面用的時候再補。
第四步:開 Claude Code 在 workspace 根目錄
1 | cd my-system-workspace |
第一個指令可以是「請讀 CLAUDE.md,然後幫我畫出整個系統的資料流」。如果你的 CLAUDE.md 寫得好,它應該能直接畫出 ASCII 圖。
實戰:跨 repo 任務怎麼跑
光有地圖還不夠,重點是能用它做事。三個常見場景。
場景一:跨服務 schema 變更
你想在 events.enriched 這個 Kafka topic 的 schema 加一個 user_segment 欄位。傳統做法:開 event-ingestion-service 改 schema,去 stream-processor 改處理邏輯,去 analytics-api 改查詢介面,每個 repo 開一個 PR。
有了 Virtual Monorepo:
1 | 你:在 events.enriched 加 user_segment 欄位, |
Claude 會自己跟著地圖去看 producer、consumer、和下游消費者,列出所有需要改的檔案。它不能幫你開 PR(每個 repo 還是要各自 commit),但它可以給你完整的 todo list。
場景二:跨服務 bug 追查
ops dashboard 顯示某個指標不對。指標的計算經過了三個服務。
1 | 你:ops-dashboard 上的 daily-active-users 數字 |
Claude 從 frontends/ops-dashboard 開始看 query,跟著 API 呼叫到 services/analytics-api,再追到 services/stream-processor 的去重邏輯,最後可能發現是 event-ingestion-service 改了 client 端的事件命名。沒有跨 repo 視野,這條追查鏈每一段都得你手動切換 cwd。
場景三:infrastructure 跟 application 一起改
你發現 Kafka 的某個 topic partition 數不夠。partition 數定義在 infrastructure/streaming-cluster/terraform/topics.tf,但消費這個 topic 的服務 services/stream-processor 也假設了 partition 數做了 hash routing。
地圖讓 Claude 同時看到這兩端,給你一份「Terraform 改完之後,application 還要改哪些常數」的 checklist。
兩個常見的踩坑點
坑一:CLAUDE.md 寫太長
第一次寫的人很容易把所有架構決策、歷史脈絡、命名約定都塞進 org 層級的 CLAUDE.md。然後跑 Claude Code 就會發現 context window 立刻吃掉一半,剩下的空間不夠 Claude 真正讀程式碼。
原則:org 層級只寫「導航資訊」——服務在哪、做什麼、依賴誰。決策脈絡放 README。命名約定放 team 層級。
坑二:忘記 sync repo
.repos 腳本只負責第一次 clone。之後每個服務的 main branch 一直在動,你 workspace 裡的副本可能過時。建議搭配 mani 或 meta 這類 multi-repo 工具,可以一行指令 mani sync 把所有 repo git pull。
如果你不想多裝工具,最低需求是寫一個 sync.sh:
1 | for dir in services/* infrastructure/* frontends/*; do |
跟其他 AI 工具比的差異
Cursor 和 GitHub Copilot 也支援多 repo workspace。差異在於:
- Cursor 預設行為是「開哪個 workspace 看哪些檔案」,沒有層級式的 context 設計。你可以用
.cursorrules但只有一層,且不會自動繼承。 - GitHub Copilot 的 context 主要靠開啟的 tab 和最近編輯過的檔案,沒有顯式的「系統地圖」概念。
- Claude Code 的
CLAUDE.md是顯式設計成可以分層繼承的,這是 Virtual Monorepo 模式能成立的關鍵。
如果你的系統真的是 5 個 repo 以下,三種工具差不多。但 repo 數量越多,分層 context 的價值越明顯。
學會這個之後可以接著做什麼
理解了 Virtual Monorepo 之後,下一步可以往三個方向延伸。
方向一:把這個地圖跟 MCP 整合。寫一個 MCP server,把 CLAUDE.md 裡定義的服務關係變成可查詢的 graph。Claude 不只「讀地圖」,還能「查地圖」——例如問「找出所有依賴 user-service 的服務」,MCP 直接給結構化答案。
方向二:用 git worktree 開平行 session。一邊讓 Claude 在 services/A 改 schema,另一邊讓另一個 Claude 在 services/B 改對應的 consumer。各自有獨立 context,互不污染。之前寫過 Claude Code Git Worktrees 完整教學有更深入的細節。
方向三:把地圖當文件來自動化維護。讓 Claude 每次開新 PR 時,順便檢查 CLAUDE.md 裡的依賴描述是不是還準確。設計成 CI step,地圖就不會腐化。
回到一開始的場景:那個 35 個 repo 的系統,第一個禮拜你需要花一兩天寫地圖,但之後每次跨服務任務都會省回大量切換 cwd 和重新跟 AI 解釋脈絡的時間。投資跟回收的比例,比合併成 monorepo 划算太多。
原文來源:The “Virtual Monorepo” Pattern — Owen Zanzal, Medium
原文來源:Structuring Claude Code for Multi-Repo Workspaces — karun.me
參考來源:Claude Code Docs — Overview










