当项目代码超过10万行、packages目录下堆叠着十几个子模块,Claude Code的200K上下文窗口瞬间变得捉襟见肘。本教程针对Monorepo场景,提炼出七条经过生产验证的治理策略,覆盖CLAUDE.md分层配置、子Agent分工、上下文预算管理、增量原子提交、依赖关系治理、跨包重构安全网、自动化质量门禁。每条策略配以可复制的配置文件和命令行操作,适合正在或即将将Claude Code引入大型Monorepo的中高级开发者。
适用人群:在Monorepo(Turborepo / Nx / pnpm workspace / Bazel)中使用Claude Code的开发者和Tech Lead。
学习目标:读完本教程,你能独立将Claude Code配置到一个50万行级别的Monorepo中,实现子包独立编码、跨包安全重构、Token消耗可控的工程化AI编码工作流。
| 项目 | 要求 |
|---|---|
| Claude Code | >= 1.0.309(支持嵌套CLAUDE.md和--subagent-type) |
| Node.js | >= 18 |
| 包管理器 | pnpm workspace / Turborepo / Nx 三选一 |
| Git | >= 2.38(支持worktree) |
| 基础知识 | 熟悉Monorepo概念、CLAUDE.md基本写法 |
Monorepo的核心矛盾:全局规则要统一,局部规则要灵活。一个根目录的CLAUDE.md如果塞进所有子包的细节,Token消耗会直接突破上下文窗口。Anthropic官方博客明确指出,成功的Claude Code部署都采用分层记忆架构。
monorepo/
├── CLAUDE.md ← 第1层:全局规则(~500 token)
├── .claude/
│ └── settings.json ← 全局工具权限
├── packages/
│ ├── shared-ui/
│ │ └── CLAUDE.md ← 第2层:包级规则(~300 token)
│ ├── api-server/
│ │ └── CLAUDE.md ← 第2层
│ └── admin-web/
│ ├── CLAUDE.md ← 第2层
│ └── src/
│ └── dashboard/
│ └── CLAUDE.md ← 第3层:模块级(~200 token)
根文件只写三条内容:架构约束、技术栈声明、禁止事项。
# 项目架构
Monorepo结构,pnpm workspace管理。
## 技术栈
- Runtime: Node.js 20 + TypeScript 5.4
- 前端: React 18 + Vite + TailwindCSS
- 后端: Fastify + Prisma + PostgreSQL
- 共享: @mono/shared-ui (组件库), @mono/types (类型定义)
## 架构规则
1. packages/shared-ui 的组件禁止引入任何后端依赖
2. 所有API路由必须定义在 @mono/types 的 schema 中
3. 数据库变更必须生成 Prisma migration,禁止手动SQL
4. 测试文件与源文件同目录,命名为 *.test.ts
## 禁止事项
- 禁止在 api-server 中直接引用 admin-web 的代码
- 禁止使用 any 类型
- 禁止跳过 ESLint 规则(不允许 eslint-disable)
关键原则:根CLAUDE.md不超过500 token。一个团队的实战报告显示,47K字的CLAUDE.md会触发Claude Code的警告,且基线Token消耗达20K(占200K上下文的10%)。
每个子包的CLAUDE.md描述本包的专属规则:
# packages/api-server/CLAUDE.md
## 本包说明
Fastify HTTP API服务。入口 src/index.ts,路由在 src/routes/。
## 关键约定
- 路由文件按 RESTful 资源命名:users.ts, orders.ts
- 每个 route handler 必须包含 Zod schema 验证
- 错误处理统一使用 src/errors/ 下的自定义错误类
- 数据库操作只在 src/services/ 层,路由层禁止直接调用 PrismaClient
## 常用命令
- `pnpm --filter api-server dev` — 启动开发服务器
- `pnpm --filter api-server test` — 运行测试
- `pnpm --filter api-server prisma:migrate` — 数据库迁移
## 依赖关系
- 依赖 @mono/types(API schema)
- 依赖 @mono/shared-logger(日志工具)
- 被依赖:无(API是终端节点)
仅在复杂模块中使用,比如认证模块、支付模块等边界清晰的子系统:
# packages/api-server/src/auth/CLAUDE.md
## 认证模块
JWT + Refresh Token双令牌方案。
## 关键文件
- jwt.ts — 签发和验证逻辑
- guards.ts — Fastify认证守卫
- strategies/ — OAuth provider策略
## 规则
- Token过期时间:access 15min, refresh 7d
- Refresh Token必须存入Redis,登出时主动删除
- 密码使用 bcrypt (cost=12) 存储
Q:CLAUDE.md总Token超了怎么办?
精简策略:每层只保留"规则"而非"解释"。将冗长的架构文档放在docs/目录,在CLAUDE.md中只写参考 docs/architecture.md,让Claude Code需要时自行读取。
Q:子包规则和根规则冲突怎么办?
Claude Code的加载优先级是就近原则——模块级 > 包级 > 根级。如果模块CLAUDE.md写了"允许any类型",它会覆盖根级"禁止any"的规则。因此建议根级只写绝对不可违反的硬约束。
在大型Monorepo中,让一个Claude Code会话处理所有任务会遇到两个问题:
┌─────────────────────────────────────────┐
│ Git Main Branch │
│ monorepo (所有代码) │
└──────────┬──────────┬───────────────────┘
│ │
┌──────▼──┐ ┌───▼────────┐
│worktree1│ │ worktree2 │
│api-server│ │ admin-web │
│ 修改路由 │ │ 重构组件 │
└──────┬──┘ └───┬────────┘
│ │
┌──────▼──────────▼────────┐
│ 合并回 Main │
│ (主会话处理冲突) │
└──────────────────────────┘
操作步骤:
# 1. 为api-server创建独立worktree
git worktree add .claude/worktrees/feat-api-v2 -b feat/api-v2
# 2. 启动Claude Code,在worktree中工作
cd .claude/worktrees/feat-api-v2
claude
# 3. 在Claude Code中指定只操作api-server
> 只修改 packages/api-server/ 下的文件。参考 CLAUDE.md 中的路由约定。
当需要多个子包同时修改时(例如给所有包添加ESLint规则),使用Task工具调度子Agent:
> 使用Task工具并行完成以下任务:
> 1. 在 packages/shared-ui 中,给所有组件添加 displayName
> 2. 在 packages/api-server 中,给所有路由添加 OpenAPI注释
> 3. 在 packages/admin-web 中,把所有 className 硬编码替换为 TailwindCSS 变量
>
> 每个任务使用独立的子Agent,避免上下文交叉。
Claude Code会为每个任务启动一个独立的子Agent,每个子Agent只加载对应子包的上下文。这样三个任务并行执行,互不干扰。
Q:子Agent修改了同一个文件怎么办?
如果多个子Agent需要修改同一个共享文件(如@mono/types的schema),改用串行模式:先让主Agent修改共享文件,再并行启动子Agent处理各自的子包。
Q:Worktree太多怎么管理?
# 查看所有worktree
git worktree list
# 清理已合并的worktree
git worktree prune
建议同时活跃的worktree不超过3个,每完成一个PR就清理。
Claude Code的上下文窗口是200K token。在Monorepo中,Token消耗的组成:
总上下文 = 系统提示(~3K) + CLAUDE.md加载(~1K)
+ 对话历史(递增) + 文件读取(按需) + 工具输出(按需)
一个团队实测报告:47K字的CLAUDE.md就消耗20K基线Token,加上几轮对话后,实际可用于文件读取的Token只剩约80K。
| 操作 | 预估Token消耗 | 备注 |
|---|---|---|
| 加载根CLAUDE.md | 200-500 | 精简时 |
| 加载包级CLAUDE.md | 100-300 | 每个包 |
| 读取一个中等源文件 | 300-800 | 100-200行 |
| Grep搜索结果 | 100-2000 | 取决于匹配数 |
| 一次完整对话轮次 | 500-2000 | 含工具调用 |
| 生成一个新文件 | 500-3000 | 取决于文件大小 |
规则1:禁止全量扫描
# ❌ 错误:让Claude Code扫描整个Monorepo
> 找出所有需要修改的文件
# ✅ 正确:先缩小范围再操作
> 在 packages/api-server/src/routes/ 目录下,找出所有缺少错误处理的路由
规则2:用Glob代替Grep预览
# ✅ 先用Glob定位文件列表(Token消耗极低)
> 列出 packages/shared-ui/src/components/ 下所有 .tsx 文件
# 然后只读取需要修改的文件
> 读取 Button.tsx 和 Modal.tsx,给它们添加 aria-label 属性
规则3:分阶段操作
# 阶段1:规划(不读取文件)
> 列出重构 packages/api-server 的认证模块需要的步骤,只列步骤不执行
# 阶段2:确认后执行
> 按你列出的步骤执行前3步
# 阶段3:验证
> 运行测试验证修改
规则4:定期清理对话
当Claude Code提示上下文窗口快满时,使用/compact命令压缩历史:
> /compact
这会保留关键上下文(已读取的文件、已做的决策),但删除冗余的中间过程。
在Monorepo中,一次跨10个文件的修改如果出错,回滚成本极高。正确的做法是原子提交:每个逻辑单元一次提交。
假设需要给所有API路由添加请求日志:
# Step 1: 先修改共享的日志工具
> 在 packages/shared-logger/src/ 中添加 requestLogger 函数。
> 接口:(req: FastifyRequest) => void
> 输出格式:[${timestamp}] ${method} ${url} - ${statusCode}
# Step 2: 提交共享工具
git add packages/shared-logger/
git commit -m "feat(logger): add requestLogger for API routes"
# Step 3: 修改路由文件
> 在 packages/api-server/src/routes/ 的每个路由文件中,
> 引入 requestLogger 并添加 onRequest 钩子。
> 每修改一个路由文件就告诉我,我来确认。
# Step 4: 逐文件提交
git add packages/api-server/src/routes/users.ts
git commit -m "feat(api): add request logging to users route"
# ... 重复每个路由文件
--max-turns控制修改粒度# 限制Agent只做3轮操作,避免一次改太多
claude -p --max-turns 3 "给 packages/api-server/src/routes/users.ts 添加请求日志"
Q:Claude Code一次性改了太多文件怎么回退?
# 查看最近的修改
git diff HEAD~1
# 如果整个提交需要回退
git revert HEAD
# 如果只需要回退某些文件
git checkout HEAD~1 -- packages/api-server/src/routes/users.ts
建议在settings.json中开启确认模式:
{
"permissions": {
"allow": [],
"deny": ["Bash(rm -rf*)"]
}
}
Monorepo中,包与包之间的依赖形成有向无环图(DAG)。Claude Code如果不理解这个图,可能产生循环依赖或破坏层级关系。
## packages/api-server/CLAUDE.md
## 依赖关系图(必须遵守)
@mono/api-server
├── @mono/types(API schema定义)
├── @mono/shared-logger(日志)
├── @mono/auth-lib(认证工具)
└── @mono/db-client(数据库客户端)
## 依赖规则
1. 本包是终端节点,不对外暴露
2. 只能依赖 @mono/* 的包,不能直接引用其他包的源码
3. 如果 @mono/types 中的 schema 需要变更,先提 PR 到 types 包
创建一个自定义命令来验证依赖关系:
# .claude/commands/verify-deps.md
验证当前修改是否违反了 CLAUDE.md 中声明的依赖规则。
检查步骤:
1. 读取 git diff --name-only 获取修改的文件列表
2. 对每个修改的文件,检查其 import 语句
3. 如果发现跨包直接引用(未通过 workspace 包名引用),报告违规
4. 如果发现依赖了未在 CLAUDE.md 中声明的包,报告违规
运行方式:
> /project:verify-deps
madge检测循环依赖# 安装madge
pnpm add -Dw madge
# 检测循环依赖
npx madge --circular packages/
# 在CI中加入检查
npx madge --circular --warning packages/ || exit 1
假设要将@mono/types中的User接口重命名为Account。这个改动会影响所有依赖@mono/types的包。直接让Claude Code一次性修改所有文件,既容易遗漏又容易出错。
flowchart TD
A[Step 1: 修改共享层] --> B[提交变更]
B --> C[Step 2: 逐包修复编译错误]
C --> D{还有编译错误?}
D -->|是| E[修复下一个包]
E --> D
D -->|否| F[Step 3: 运行全量测试]
F --> G{测试通过?}
G -->|是| H[合并]
G -->|否| I[修复测试]
I --> F
Step 1:修改共享层,先让类型兼容
// packages/types/src/user.ts
// 重命名时保留旧名称作为别名
export interface Account {
id: string;
email: string;
name: string;
}
/** @deprecated 使用 Account 替代 */
export type User = Account;
git add packages/types/
git commit -m "feat(types): rename User to Account, keep User alias"
Step 2:逐包迁移
# 使用-p模式逐包处理
for pkg in api-server admin-web worker; do
claude -p --max-turns 5 \
"将 packages/$pkg 中所有对 User 类型的引用改为 Account。
不要修改 packages/types/ 下的文件。
修改完后运行 pnpm --filter $pkg typecheck 验证。"
done
Step 3:删除旧别名,全量验证
# 删除废弃别名
claude -p --max-turns 3 \
"删除 packages/types/src/user.ts 中的 User 别名导出。
然后运行 pnpm -r typecheck 和 pnpm -r test 验证。"
在大型Monorepo中,TypeScript项目引用(Project References)能显著加速类型检查:
// packages/api-server/tsconfig.json
{
"references": [
{ "path": "../types" },
{ "path": "../shared-logger" }
]
}
这样tsc --build只重新编译有变更的包,而不是整个Monorepo。
在大型Monorepo中,必须阻止低质量代码进入代码库。使用Claude Code的Hook系统:
// .claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "bash .claude/hooks/quality-check.sh \"$CLAUDE_FILE_PATH\""
}
]
}
}
#!/bin/bash
# .claude/hooks/quality-check.sh
FILE="$1"
# 1. 检查是否误改了其他包的文件
CURRENT_PKG=$(echo "$FILE" | grep -oP 'packages/[^/]+')
ALLOWED_PKGS=$(cat .claude/current-work-package 2>/dev/null)
if [ -n "$ALLOWED_PKGS" ] && [ "$CURRENT_PKG" != "$ALLOWED_PKGS" ]; then
echo "⚠️ 警告:正在修改 $CURRENT_PKG,但当前工作范围是 $ALLOWED_PKGS"
fi
# 2. 检查是否引入了 console.log
if grep -q "console\.log" "$FILE" 2>/dev/null; then
echo "⚠️ 发现 console.log,请使用 @mono/shared-logger 替代"
fi
# 3. 检查是否有未处理的 TODO
if grep -q "TODO\|FIXME\|HACK" "$FILE" 2>/dev/null; then
echo "ℹ️ 文件包含 TODO/FIXME/HACK 标记"
fi
使用GitHub Actions自动运行Claude Code做代码审查:
# .github/workflows/ai-review.yml
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed packages
id: packages
run: |
CHANGED=$(git diff --name-only origin/main...HEAD | \
grep -oP 'packages/[^/]+' | sort -u)
echo "list=$CHANGED" >> $GITHUB_OUTPUT
- name: Run Claude Code review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
for pkg in ${{ steps.packages.outputs.list }}; do
# 只审查变更的文件
FILES=$(git diff --name-only origin/main...HEAD -- "$pkg/")
claude -p --max-turns 3 \
--output-format json \
"审查以下文件的变更,重点关注:
1. 是否违反了 $pkg/CLAUDE.md 中的规则
2. 是否有跨包违规引用
3. 是否缺少错误处理
文件列表:$FILES
使用 git diff origin/main...HEAD -- $FILES 查看具体变更。"
done
在CLAUDE.md中嵌入质量检查清单,每次提交前由Claude Code自动校验:
## 提交前检查清单
每次修改代码后,自动执行以下检查:
- [ ] `pnpm -r typecheck` — 类型检查通过
- [ ] `pnpm -r lint` — ESLint检查通过
- [ ] `pnpm -r test` — 所有测试通过
- [ ] 无跨包违规引用
- [ ] 新代码有对应的测试
- [ ] 无 console.log 或 debugger 残留
下面用一个完整案例串联以上七条策略。
一个典型的pnpm workspace Monorepo:
i18n-monorepo/
├── CLAUDE.md
├── pnpm-workspace.yaml
├── packages/
│ ├── shared-ui/ # React组件库
│ ├── api-server/ # Fastify API
│ ├── admin-web/ # 管理后台
│ └── types/ # 共享类型
需求:为整个项目添加i18n(国际化)支持,首期支持中英文。
阶段1:规划(使用上下文预算)
> 列出为这个Monorepo添加i18n支持需要的步骤。
> 不要执行任何修改,只输出步骤列表。
> 考虑以下方面:翻译文件结构、运行时切换、组件适配、API错误消息。
Claude Code输出规划:
1. 在 packages/types 中定义 I18nLocale 类型和翻译 key 的 schema
2. 创建 packages/shared-i18n 包(翻译加载器 + React hook)
3. 在 packages/shared-ui 中适配组件,接受 i18n props
4. 在 packages/api-server 中实现错误消息的国际化
5. 在 packages/admin-web 中添加语言切换 UI
阶段2:修改共享层(策略一 + 策略四)
# 先修改 types 包
cd packages/types
claude
> 在 src/ 中创建 i18n.ts,定义以下类型:
> - Locale: 'zh' | 'en'
> - TranslationKey: 所有翻译 key 的联合类型
> - TranslationDict: Record<Locale, Record<TranslationKey, string>>
> 参考本包 CLAUDE.md 的类型导出规范。
git add packages/types/src/i18n.ts
git commit -m "feat(types): add i18n type definitions"
阶段3:创建新包(策略二 + 策略五)
# 创建 shared-i18n 包
cd ../..
mkdir -p packages/shared-i18n/src
先写好这个新包的CLAUDE.md:
# packages/shared-i18n/CLAUDE.md
## 本包说明
国际化运行时工具包。提供翻译加载、语言切换、React Hook。
## 关键约定
- 翻译文件放在 src/locales/,按语言命名:zh.ts, en.ts
- 使用 @mono/types 中定义的 TranslationKey 类型确保 key 一致
- 导出 useTranslation React Hook
- 导出 I18nProvider React Context Provider
## 依赖
- @mono/types(类型定义)
- react(peer dependency)
## 被依赖
- @mono/shared-ui
- @mono/admin-web
然后让Claude Code填充实现:
> 根据 CLAUDE.md 的描述,实现这个 i18n 包的完整代码。
> 包含:翻译加载器、I18nProvider、useTranslation hook、
> zh.ts 和 en.ts 两个语言文件(先放空壳翻译)。
阶段4:并行适配UI和API(策略二:子Agent并行)
# 终端1:适配 shared-ui 组件
cd packages/shared-ui
claude
> 将 src/ 下的所有组件中的硬编码中文文案替换为 useTranslation() 调用。
> 例如 "确定" → t('common.confirm')
> 不要修改组件的 props 接口,只替换文案。
> 每修改完一个组件就运行 pnpm --filter shared-ui typecheck 验证。
# 终端2:适配 api-server
cd packages/api-server
claude
> 修改错误处理,将硬编码的错误消息改为支持 i18n。
> 在 src/errors/ 下创建 I18nError 类,接受 TranslationKey 而非固定字符串。
> 修改中间件,根据请求头 Accept-Language 决定错误消息语言。
阶段5:质量验证(策略七)
> 执行提交前检查清单:
> 1. 运行 pnpm -r typecheck
> 2. 运行 pnpm -r lint
> 3. 运行 pnpm -r test
> 4. 检查是否有跨包违规引用
> 5. 确认所有新代码都有测试
> 报告每项的结果。
flowchart TB
subgraph "阶段1: 规划"
A[读取CLAUDE.md] --> B[输出步骤列表]
B --> C[用户确认]
end
subgraph "阶段2: 共享层"
C --> D[修改 @mono/types]
D --> E[创建 @mono/shared-i18n]
E --> F[提交共享层变更]
end
subgraph "阶段3: 并行适配"
F --> G[子Agent: 适配 shared-ui]
F --> H[子Agent: 适配 api-server]
F --> I[子Agent: 适配 admin-web]
end
subgraph "阶段4: 集成验证"
G --> J[类型检查]
H --> J
I --> J
J --> K[全量测试]
K --> L[代码审查]
L --> M[合并]
end
style A fill:#e1f5fe
style F fill:#c8e6c9
style J fill:#fff3e0
style M fill:#c8e6c9
| 策略 | 核心原则 | 关键配置 | Token节省 |
|---|---|---|---|
| 1. 分层CLAUDE.md | 全局统一+局部灵活 | 三级嵌套CLAUDE.md | ~40% |
| 2. 子Agent分工 | 隔离上下文、并行执行 | worktree + Task工具 | ~30% |
| 3. 上下文预算 | 先定位后读取、分阶段操作 | /compact + --max-turns | ~50% |
| 4. 增量原子提交 | 小步快跑、可随时回滚 | git add 单文件提交 | 降低风险 |
| 5. 依赖关系治理 | DAG约束、防止循环 | CLAUDE.md声明+madge检测 | 防止架构腐化 |
| 6. 跨包重构安全网 | 别名兼容→逐包迁移→全量验证 | TypeScript项目引用 | 降低重构风险 |
| 7. 自动化质量门禁 | Hook+CI+清单三重保障 | settings.json hooks | 质量保证 |
| 维度 | 评估 |
|---|---|
| 适合训练营类型 | 企业内训、高级工程师进阶营 |
| 前置技能要求 | Claude Code基础使用、Monorepo经验、Git进阶 |
| 预计教学时长 | 4-6小时(含实操) |
| 实操占比 | 70%(7个策略均有动手练习) |
| 建议分组 | 2-3人一组,每组一个子包 |
| 硬件要求 | 每人一台开发机,安装Claude Code |
| 预算考量 | 每人预计消耗 $5-10 API费用 |
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
