Advanced (更新: 2026/6/1)

Claude Code 管理 Monorepo 实战:pnpm、Turborepo/Nx 与 CI

用 Claude Code 安全管理 Monorepo:仓库地图、pnpm workspace、Turborepo/Nx affected、CODEOWNERS 与 CI 实例。

Claude Code 管理 Monorepo 实战:pnpm、Turborepo/Nx 与 CI

Monorepo 是把多个应用和库放在一个 Git 仓库中管理的结构。它可以让 UI、类型、工具配置和 CI 复用得更干净,但如果边界不清,Claude Code 很容易把「顺手修一下」变成跨应用的大改动。

可靠的流程是:先让 Claude Code 画出仓库地图,再定义 package boundary,也就是每个包允许依赖谁;用 pnpm workspaceworkspace:* 固定内部依赖;再用 Turborepo 或 Nx 的 affected tasks 只检查受影响的范围。相关官方资料可以参考 Nx 为什么使用 monorepoNx affectedNx mental modelpnpmTurborepo 文档

目标结构

不要一开始就让 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 个实用场景

  1. 修改 packages/ui 的 Button。让 Claude Code 保持公开 API 不变,只新增 loading 状态,并列出 apps/web 中受影响页面。

  2. 把 API 和前端共用的 DTO 放入 packages/shared。DTO 是对外传输的数据形状,不等于数据库模型;数据库模型不要放进 shared。

  3. 升级 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 workspaceClaude Code 与 pnpm workspaceClaude Code 与 TurborepoClaude 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。

#Claude Code #Monorepo #pnpm workspace #Turborepo #Nx
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。