Claude Code Monorepo 策略
在 Monorepo 中高效使用 Claude Code 的配置与策略
Monorepo 遇上 AI
Monorepo 是现代大型项目的主流架构——前端、后端、共享库、工具链全部放在一个仓库里。Google、Meta、Microsoft 都在用。
但 Monorepo 对 AI 编码工具来说是个挑战:
- 代码量巨大,上下文窗口装不下
- 包之间有复杂的依赖关系
- 不同包可能用不同的技术栈
- 构建和测试流程复杂
今天我们来看看如何在 Monorepo 中高效使用 Claude Code。
典型 Monorepo 结构
my-monorepo/
├── CLAUDE.md ← 根级项目记忆
├── package.json ← 根 package.json
├── turbo.json / nx.json ← 构建编排配置
├── packages/
│ ├── web/ ← 前端应用
│ │ ├── CLAUDE.md ← 包级项目记忆
│ │ ├── package.json
│ │ └── src/
│ ├── api/ ← 后端服务
│ │ ├── CLAUDE.md
│ │ ├── package.json
│ │ └── src/
│ ├── shared/ ← 共享库
│ │ ├── CLAUDE.md
│ │ ├── package.json
│ │ └── src/
│ └── config/ ← 共享配置
│ ├── eslint/
│ ├── tsconfig/
│ └── tailwind/
├── tools/ ← 内部工具
│ └── scripts/
└── .claude/
└── settings.json
CLAUDE.md 层级策略
在 Monorepo 中,CLAUDE.md 的层级设计至关重要。
根级 CLAUDE.md
放全局信息,保持精简:
# My Monorepo
## 概述
这是一个全栈 SaaS 产品的 Monorepo,包含 Web 前端、API 后端和共享库。
## 技术栈概览
| 包 | 技术 | 说明 |
|-----|------|------|
| packages/web | Next.js 15, React 19, Tailwind CSS 4 | 前端应用 |
| packages/api | Hono, Drizzle ORM, PostgreSQL | 后端 API |
| packages/shared | TypeScript | 共享类型和工具函数 |
## 包管理
- 使用 pnpm workspace
- 构建工具:Turborepo
## 常用命令
```bash
pnpm install # 安装所有依赖
pnpm dev # 启动所有开发服务器
pnpm build # 构建所有包
pnpm test # 运行所有测试
pnpm lint # 检查所有包
包间依赖
- web 依赖 shared
- api 依赖 shared
- shared 不依赖其他包
重要约定
- 跨包类型定义放在 packages/shared/src/types/
- 跨包修改需要同时更新所有受影响的包
- 每个包有独立的 tsconfig.json,继承根配置
### 包级 CLAUDE.md
每个包有自己的 CLAUDE.md,写包特定的信息:
```markdown
# packages/web
## 技术栈
- Next.js 15 (App Router)
- React 19
- Tailwind CSS 4
- Zustand (状态管理)
## 目录结构
src/ ├── app/ # App Router 页面 ├── components/ # React 组件 │ ├── ui/ # 基础 UI 组件 │ └── features/ # 业务组件 ├── hooks/ # 自定义 Hooks ├── lib/ # 工具函数 └── styles/ # 全局样式
## 常用命令
```bash
pnpm --filter web dev # 启动开发服务器
pnpm --filter web build # 构建
pnpm --filter web test # 运行测试
pnpm --filter web lint # Lint 检查
约定
- 组件使用 PascalCase
- 页面组件放在 app/ 目录
- 服务端组件默认,客户端组件加 ‘use client’
- API 调用通过 lib/api.ts 统一管理
```markdown
# packages/api
## 技术栈
- Hono (Web 框架)
- Drizzle ORM
- PostgreSQL
- Zod (参数校验)
## 目录结构
src/ ├── routes/ # API 路由 ├── middleware/ # 中间件 ├── db/ # 数据库相关 │ ├── schema.ts # Drizzle Schema │ └── migrate/ # 迁移文件 ├── services/ # 业务逻辑 └── utils/ # 工具函数
## 常用命令
```bash
pnpm --filter api dev # 启动开发服务器
pnpm --filter api db:migrate # 运行数据库迁移
pnpm --filter api db:seed # 填充测试数据
pnpm --filter api test # 运行测试
约定
- 路由文件以 .route.ts 结尾
- 所有请求参数用 Zod schema 校验
- 数据库操作封装在 services/ 中
- 不在路由中直接操作数据库
---
## 上下文范围控制
Monorepo 最大的挑战是上下文管理。我们需要让 Claude Code 只关注当前工作的包。
### 策略 1:从包目录启动
```bash
# 不要从根目录启动
cd my-monorepo
claude # 上下文太大
# 从具体包目录启动
cd my-monorepo/packages/web
claude # 上下文聚焦在 web 包
策略 2:在提示中明确范围
我现在在 packages/api 中工作。
帮我给 src/routes/users.route.ts 添加分页功能。
不需要修改其他包。
策略 3:使用 .claudeignore 排除无关包
在每个包目录下创建 .claudeignore:
# packages/web/.claudeignore
# 排除其他包的源码(保留 shared 的类型定义)
../api/src/
../config/
../../tools/
../../node_modules/
策略 4:Turborepo 的 —filter 思维
像 Turborepo 的 --filter 一样,告诉 Claude Code 只关注特定包:
只关注 packages/shared 包。
我需要在 src/types/user.ts 中添加一个新的用户角色类型,
然后确保 packages/web 和 packages/api 中使用这个类型的地方都更新了。
跨包修改
Monorepo 的一个常见场景是修改共享库后,需要同步更新所有依赖包。
类型变更的连锁反应
假设我们要在 shared 包中添加一个新字段:
在 packages/shared/src/types/user.ts 中给 User 类型添加 avatar 字段,
然后更新所有使用 User 类型的地方。
Claude Code 会:
- 修改 shared 包的类型定义:
// packages/shared/src/types/user.ts
export interface User {
id: string;
name: string;
email: string;
avatar: string | null; // 新增
createdAt: Date;
}
- 更新 API 包的数据库 schema:
// packages/api/src/db/schema.ts
export const users = pgTable('users', {
id: uuid('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
avatar: text('avatar'), // 新增
createdAt: timestamp('created_at').defaultNow(),
});
- 更新 Web 包的组件:
// packages/web/src/components/features/UserProfile.tsx
export function UserProfile({ user }: { user: User }) {
return (
<div>
{user.avatar ? (
<img src={user.avatar} alt={user.name} className="w-10 h-10 rounded-full" />
) : (
<div className="w-10 h-10 rounded-full bg-gray-200" />
)}
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
跨包修改的最佳实践
- 从共享层开始修改,然后向上层传播
- 修改后运行全量类型检查:
pnpm tsc --noEmit - 运行受影响包的测试:
pnpm --filter web --filter api test
CI 集成
Turborepo + Claude Code
在 CI 中利用 Turborepo 的缓存和 Claude Code 的自动化:
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- run: pnpm install
# 利用 Turborepo 只构建受影响的包
- run: pnpm turbo build --filter='...[origin/main]'
- run: pnpm turbo test --filter='...[origin/main]'
- run: pnpm turbo lint --filter='...[origin/main]'
ai-review:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: AI Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
# 只审查变更的文件
CHANGED_FILES=$(git diff --name-only origin/main...HEAD)
claude --print "审查以下文件的变更,关注类型安全和跨包一致性:$CHANGED_FILES"
Nx + Claude Code
# Nx 的受影响包检测
- name: Get affected packages
run: |
AFFECTED=$(npx nx show projects --affected --base=origin/main)
echo "affected=$AFFECTED" >> $GITHUB_OUTPUT
- name: AI Review affected packages
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
claude --print "审查以下受影响包的变更:${{ steps.affected.outputs.affected }}"
Turborepo 实战配置
turbo.json 配置
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"inputs": ["src/**", "tests/**"]
},
"lint": {
"dependsOn": ["^build"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
在 CLAUDE.md 中说明构建关系
## 构建依赖关系
shared (先构建) ├── web (依赖 shared) └── api (依赖 shared)
### 构建命令
- 全量构建:`pnpm turbo build`
- 只构建 web:`pnpm turbo build --filter=web...`(包含依赖)
- 只构建变更的包:`pnpm turbo build --filter='...[HEAD~1]'`
Nx 实战配置
project.json 配置
{
"name": "web",
"targets": {
"build": {
"executor": "@nx/next:build",
"dependsOn": ["^build"],
"outputs": ["{projectRoot}/.next"]
},
"test": {
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "packages/web/jest.config.ts"
}
}
}
}
Nx 特有的 CLAUDE.md 内容
## Nx 命令
### 常用命令
```bash
npx nx build web # 构建 web 包
npx nx test api # 测试 api 包
npx nx affected -t test # 测试受影响的包
npx nx graph # 查看依赖图
代码生成
npx nx g @nx/react:component Button --project=web
npx nx g @nx/node:library utils --directory=packages/utils
---
## 实用技巧
### 1. 包级别的 Hooks
不同包可以有不同的 Hook 配置:
packages/web/.claude/settings.json packages/api/.claude/settings.json
```json
// packages/web/.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hook": {
"type": "command",
"command": "npx next lint --fix --file $CLAUDE_FILE_PATH 2>/dev/null || true"
}
}
]
}
}
// packages/api/.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hook": {
"type": "command",
"command": "npx eslint --fix $CLAUDE_FILE_PATH 2>/dev/null || true"
}
}
]
}
}
2. 共享 Slash Commands
在根目录创建团队共享的 Slash Commands:
<!-- .claude/commands/cross-package-update.md -->
更新共享类型并同步所有依赖包:
1. 修改 packages/shared 中的类型定义
2. 运行 `pnpm tsc --noEmit` 检查类型错误
3. 修复所有类型错误
4. 运行 `pnpm turbo test --filter='...[HEAD]'` 确认测试通过
5. 生成变更摘要
要更新的类型:$ARGUMENTS
使用:
/cross-package-update User 类型添加 role 字段
3. 依赖图感知
在 CLAUDE.md 中画出包的依赖关系,帮助 Claude Code 理解影响范围:
## 依赖图
config ──→ (被所有包引用) shared ──→ web, api web ──→ (无下游依赖) api ──→ (无下游依赖)
修改 shared 包时,必须检查 web 和 api 是否受影响。
修改 config 包时,必须检查所有包是否受影响。
4. 环境变量管理
Monorepo 中不同包可能需要不同的环境变量:
## 环境变量
| 变量 | 包 | 说明 |
|------|-----|------|
| DATABASE_URL | api | PostgreSQL 连接字符串 |
| NEXT_PUBLIC_API_URL | web | API 地址 |
| JWT_SECRET | api | JWT 签名密钥 |
| REDIS_URL | api | Redis 连接字符串 |
每个包有自己的 .env.example 文件。
常见问题
Q: Claude Code 在 Monorepo 根目录启动时很慢怎么办?
A: 配置 .claudeignore 排除 node_modules、dist、.next 等目录。同时精简根级 CLAUDE.md,把详细信息放在包级 CLAUDE.md 中。
Q: 跨包修改时 Claude Code 会遗漏某些包吗?
A: 可能会。在 CLAUDE.md 中明确写出依赖关系,并在提示中强调”检查所有依赖包”。修改后运行全量类型检查是最可靠的验证方式。
Q: 应该从根目录还是包目录启动 Claude Code?
A: 如果只在一个包内工作,从包目录启动更高效。如果需要跨包修改,从根目录启动,但要在提示中明确范围。
Monorepo 是复杂性的集中地,但也是效率的放大器。用好 CLAUDE.md 层级、上下文范围控制和跨包协调策略,Claude Code 就能在这个复杂的环境中游刃有余——帮你管理那些人脑难以同时追踪的包间依赖。
相关文章
评论
加载中...
评论
加载中...