Claude Code 管理 Monorepo 实战:pnpm、Turborepo/Nx 与 CI
用 Claude Code 安全管理 Monorepo:仓库地图、pnpm workspace、Turborepo/Nx affected、CODEOWNERS 与 CI 实例。
Monorepo 是把多个应用和库放在一个 Git 仓库中管理的结构。它可以让 UI、类型、工具配置和 CI 复用得更干净,但如果边界不清,Claude Code 很容易把「顺手修一下」变成跨应用的大改动。
可靠的流程是:先让 Claude Code 画出仓库地图,再定义 package boundary,也就是每个包允许依赖谁;用 pnpm workspace 和 workspace:* 固定内部依赖;再用 Turborepo 或 Nx 的 affected tasks 只检查受影响的范围。相关官方资料可以参考 Nx 为什么使用 monorepo、Nx affected、Nx mental model、pnpm 和 Turborepo 文档。
目标结构
不要一开始就让 Claude Code 改代码。先让它理解结构。
graph TD
WEB["apps/web"] --> UI["packages/ui"]
WEB --> SHARED["packages/shared"]
API["apps/api"] --> SHARED
UI --> CONFIG["packages/config"]
SHARED --> CONFIG
CI["CI affected tasks"] --> WEB
CI --> API
apps/* 是可部署应用,packages/* 是可复用模块。packages/shared 只放稳定的类型、校验和纯函数,不要放数据库连接、页面 hooks 或 API 客户端。
先让 Claude Code 输出仓库地图
请把这个仓库当作 monorepo 来阅读。
前提:
- apps/web 是 Next.js 应用
- apps/api 是 API server
- packages/ui 是通用 UI
- packages/shared 是类型、校验和纯函数
- packages/config 是 ESLint、TypeScript、Prettier、测试配置
规则:
- apps/* 不能直接依赖 apps/*
- packages/* 不能依赖 apps/*
- 内部包必须使用 workspace:* 版本
- 修改后要用 affected tasks 运行 lint/test/build
请先输出仓库地图:依赖关系、可能的循环依赖、过度共享的文件、CI 验证命令。
现在不要编辑文件。
「现在不要编辑文件」很重要。Monorepo 的风险通常不在单个文件,而在依赖边界。先拿到模型,再决定是否让 Claude Code 修改。
pnpm workspace 和内部依赖
pnpm-workspace.yaml 定义工作区。
packages:
- "apps/*"
- "packages/*"
根目录的脚本要稳定,避免每个人用不同命令。
{
"name": "acme-monorepo",
"private": true,
"packageManager": "pnpm@10.12.1",
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"typecheck": "turbo run typecheck",
"ci:affected": "turbo run lint test build --affected",
"check:deps": "node scripts/check-workspace-deps.cjs"
}
}
内部依赖使用 workspace:*。
{
"dependencies": {
"@acme/shared": "workspace:*",
"@acme/ui": "workspace:*"
}
}
给 Claude Code 的请求也要包含这条规则:
让 apps/web 可以使用 @acme/ui 和 @acme/shared。
package.json 必须使用 workspace:*。
不要用 ../../packages 这种相对路径 import。
修改后要能通过 pnpm check:deps 和 pnpm ci:affected 验证。
Turborepo 与 Nx affected
Turborepo 适合把已有 package scripts 并行、缓存并按影响范围执行。turbo.json 使用当前的 tasks 写法。
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
},
"typecheck": {
"dependsOn": ["^build"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
如果团队需要更强的项目图和影响分析,可以加入 Nx。
pnpm dlx nx@latest init
pnpm nx affected -t lint test build --base=origin/main --head=HEAD
无论选择哪一个,都不要让 Claude Code 默认「全量跑一遍」。更好的指令是「根据 Git diff 只运行受影响的 lint、test、build」。
CODEOWNERS 和依赖策略
CODEOWNERS 让 review 责任固定下来。
/apps/web/ @acme/frontend
/apps/api/ @acme/backend
/packages/ui/ @acme/design-system
/packages/shared/ @acme/platform
/packages/config/ @acme/platform
/pnpm-workspace.yaml @acme/platform
/turbo.json @acme/platform
再用脚本检查内部依赖。保存为 scripts/check-workspace-deps.cjs。
const fs = require("node:fs");
const path = require("node:path");
const ROOT = process.cwd();
const WORKSPACE_DIRS = ["apps", "packages"];
const DEP_FIELDS = [
"dependencies",
"devDependencies",
"peerDependencies",
"optionalDependencies",
];
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function findPackageDirs(baseDir) {
const absoluteBase = path.join(ROOT, baseDir);
if (!fs.existsSync(absoluteBase)) return [];
return fs
.readdirSync(absoluteBase, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => path.join(absoluteBase, entry.name))
.filter((dir) => fs.existsSync(path.join(dir, "package.json")));
}
const packages = WORKSPACE_DIRS.flatMap(findPackageDirs).map((dir) => {
const manifest = readJson(path.join(dir, "package.json"));
return {
dir,
name: manifest.name,
manifest,
};
});
const byName = new Map(packages.map((pkg) => [pkg.name, pkg]));
let failed = false;
for (const pkg of packages) {
for (const field of DEP_FIELDS) {
const deps = pkg.manifest[field] || {};
for (const [name, range] of Object.entries(deps)) {
const internal = byName.get(name);
if (!internal) continue;
const fromDir = path.relative(ROOT, pkg.dir).replace(/\\/g, "/");
const toDir = path.relative(ROOT, internal.dir).replace(/\\/g, "/");
if (!String(range).startsWith("workspace:")) {
console.error(`${pkg.name}: ${name} must use workspace:* in ${field}`);
failed = true;
}
if (toDir.startsWith("apps/")) {
console.error(`${pkg.name}: ${fromDir} must not depend on app package ${toDir}`);
failed = true;
}
}
}
}
if (failed) process.exit(1);
console.log(`Checked ${packages.length} workspace packages.`);
CI 清单
name: monorepo-ci
on:
pull_request:
push:
branches: [main]
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm check:deps
- run: pnpm ci:affected
fetch-depth: 0 是关键。affected 需要 Git 历史,如果 checkout 太浅,范围判断会不准。
3 个实用场景
-
修改
packages/ui的 Button。让 Claude Code 保持公开 API 不变,只新增 loading 状态,并列出apps/web中受影响页面。 -
把 API 和前端共用的 DTO 放入
packages/shared。DTO 是对外传输的数据形状,不等于数据库模型;数据库模型不要放进 shared。 -
升级 TypeScript、Next.js 或测试工具。先从
packages/config改,再让 Claude Code 用 affected 结果说明哪些应用被影响。
第 4 个常见场景是跨团队功能,例如 billing 或 search。让 Claude Code 把工作拆成几个 PR,而不是一次改完 UI、API、schema 和日志。
常见坑
第一,packages/shared 变成杂物间。稳定、通用、容易测试的代码才适合 shared。
第二,使用 ../../packages/shared/src 这种相对路径跨边界。短期能跑,长期会破坏构建顺序和 review。
第三,同时深度引入 Turborepo 和 Nx。先选一个主模型,等 CI 或项目图真的需要时再扩展。
第四,只看本地能跑。Monorepo 里一个 app 通过,不代表所有依赖它的 app 都安全。PR 说明必须包含变更包、受影响应用、执行命令和剩余风险。
审查提示词
请从 monorepo 角度 review 这次 diff。
检查:
- apps/* 没有直接依赖 apps/*
- packages/* 没有依赖 apps/*
- 内部依赖使用 workspace:*
- packages/shared 只包含稳定共享代码
- affected lint/test/build 覆盖足够
- CODEOWNERS 的 review 责任清晰
输出:
- blocker
- 建议修复
- 已验证命令
- PR 描述中应写的影响范围
继续阅读:Claude Code 与 Nx workspace、Claude Code 与 pnpm workspace、Claude Code 与 Turborepo、Claude Code 团队协作。
如果要把 Claude Code 引入团队级 monorepo,真正要设计的是边界、owner、CI 和 review 模板。ClaudeCodeLab 可以围绕真实仓库整理 CLAUDE.md、CODEOWNERS、CI 和 PR 流程;需要时可以从 Claude Code 培训与咨询 开始。
总结
Claude Code 适合 monorepo,但前提是约束清楚。仓库地图、package boundaries、pnpm workspace、Turborepo/Nx affected tasks、CODEOWNERS、依赖策略和 CI 清单,能把一次性的 AI 输出变成可重复的工程流程。
实际试用后,收益最大的做法是强制 workspace:* 和统一 pnpm ci:affected。尤其在修改 packages/shared 时,让 Claude Code 先列出受影响应用,可以明显减少 review 漏洞和不必要的全量 CI。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code Permission Receipt Pattern:记录权限、证据和回滚方式
Claude Code 权限 receipt:记录允许动作、需要批准的边界、验证命令、回滚说明,以及 Gumroad 和咨询 CTA 检查。
Claude Code/Codex 安全 Agent Harness 实战:权限、验证与回滚
用权限策略、执行计划、验证脚本和回滚日志,为 Claude Code 与 Codex 搭建更安全的 AI Agent 工作流。
Claude Code 子代理实战指南:安全委派并行文章与代码工作
用 Claude Code 子代理安全拆分文章和代码工作:委派规则、提示词模板、失败模式与检查清单。