Claude Code | | 约 25 分钟 | 9,774 字

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 会:

  1. 修改 shared 包的类型定义:
// packages/shared/src/types/user.ts
export interface User {
  id: string;
  name: string;
  email: string;
  avatar: string | null;  // 新增
  createdAt: Date;
}
  1. 更新 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(),
});
  1. 更新 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>
  );
}

跨包修改的最佳实践

  1. 从共享层开始修改,然后向上层传播
  2. 修改后运行全量类型检查:pnpm tsc --noEmit
  3. 运行受影响包的测试: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_modulesdist.next 等目录。同时精简根级 CLAUDE.md,把详细信息放在包级 CLAUDE.md 中。

Q: 跨包修改时 Claude Code 会遗漏某些包吗?

A: 可能会。在 CLAUDE.md 中明确写出依赖关系,并在提示中强调”检查所有依赖包”。修改后运行全量类型检查是最可靠的验证方式。

Q: 应该从根目录还是包目录启动 Claude Code?

A: 如果只在一个包内工作,从包目录启动更高效。如果需要跨包修改,从根目录启动,但要在提示中明确范围。


Monorepo 是复杂性的集中地,但也是效率的放大器。用好 CLAUDE.md 层级、上下文范围控制和跨包协调策略,Claude Code 就能在这个复杂的环境中游刃有余——帮你管理那些人脑难以同时追踪的包间依赖。

评论

加载中...

相关文章

分享:

评论

加载中...