Claude Code + Turborepo:高速 Monorepo 构建实战
用Claude Code设计Turborepo monorepo,覆盖tasks、缓存、CI、提示词和常见坑。
为什么把 Claude Code 和 Turborepo 放在一起
Monorepo 是把多个应用和共享 package 放在同一个仓库里的做法。仓库刚开始只有 apps/web 和 packages/ui 时很轻松,但一旦出现管理后台、文档站、共享工具函数和多个构建目标,真正麻烦的是构建顺序、依赖边界、CI 时间和缓存命中率。
Turborepo 通过任务图和缓存解决这些问题。Claude Code 的价值不是“帮我生成一个配置文件”这么简单,而是让它读取现有 package.json,解释 package 之间的依赖,检查 outputs 是否遗漏,并把 CI 命令收敛到真正受影响的范围。
本文基于 2026 年 6 月 2 日的 Turborepo v2。现在的配置应使用 tasks,不要沿用旧文章里的 pipeline。建议对照官方资料:Turborepo configuration、turbo run、Remote Caching 和 Claude Code memory。
站内延伸可以继续看 monorepo 管理、pnpm workspace 与 Claude Code 和 Claude Code CI/CD 设置。
目标结构和三个真实场景
本文的目标是一个小型 TypeScript monorepo,底层使用 pnpm workspace,上层用 Turborepo 管理任务。不要一开始就创建巨大的 shared 或 common 包;Claude Code 更容易遵守能用一句话解释清楚的边界。
flowchart LR
web["apps/web\n用户端"] --> ui["packages/ui\n共享UI"]
admin["apps/admin\n管理端"] --> ui
web --> utils["packages/utils\n共享函数"]
admin --> utils
ui --> tsconfig["packages/tsconfig\nTS配置"]
utils --> tsconfig
第一个场景是用户端和管理端共用按钮、表单和校验逻辑。把 packages/ui 和 packages/utils 分开后,Claude Code 不容易把视觉组件、业务函数和 API 调用混在一起。
第二个场景是内容站或文档站。Landing page、文档、管理后台经常在同一个仓库中演进。如果每次 PR 都全量构建,CI 会越来越慢。--affected 和 --filter 可以只验证变更 package 以及受它影响的 package。
第三个场景是 SaaS 或带收入转化的产品。价格页、注册表单、账户设置和管理功能往往共享小 package。只要 lint、type-check、test、build 稳定,Claude Code 修改 CTA 或 onboarding 时,CI 仍能挡住破坏共享代码的改动。
先复制最小仓库结构
先固定目录结构,再让 Claude Code 做增量修改。重点不是目录多,而是职责清楚。
acme-monorepo/
apps/
web/
package.json
src/
admin/
package.json
src/
packages/
ui/
package.json
src/
utils/
package.json
src/
tsconfig/
package.json
base.json
pnpm-workspace.yaml
package.json
turbo.json
CLAUDE.md
pnpm-workspace.yaml 保持简单即可。
packages:
- "apps/*"
- "packages/*"
根目录 package.json 负责把常用命令统一成 Turborepo 命令。2026 年 6 月确认到的发布版本是 turbo@2.9.16 和 pnpm@11.5.1,实际项目请以 lockfile 固定结果。
{
"name": "acme-monorepo",
"private": true,
"packageManager": "pnpm@11.5.1",
"scripts": {
"dev": "turbo dev",
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"type-check": "turbo run type-check",
"check": "turbo run lint type-check test build",
"check:affected": "turbo run lint type-check test build --affected"
},
"devDependencies": {
"turbo": "^2.9.16",
"typescript": "^5.8.3"
}
}
内部 package 用 workspace:*,明确告诉包管理器从当前 workspace 解析,而不是去 registry 找同名包。
{
"name": "@acme/ui",
"version": "0.1.0",
"private": true,
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "eslint src --max-warnings=0",
"type-check": "tsc -p tsconfig.json --noEmit",
"test": "vitest run"
},
"devDependencies": {
"@acme/tsconfig": "workspace:*",
"typescript": "^5.8.3",
"vitest": "^3.1.0",
"eslint": "^9.25.0"
}
}
turbo.json 要写清 tasks、outputs 和 env
根目录的 turbo.json 是整个仓库的任务契约。tasks 下的每个键,都会匹配各 package 的同名脚本。^build 的意思是先构建依赖 package,再构建当前 package。
{
"$schema": "https://turborepo.dev/schema.json",
"globalDependencies": ["pnpm-lock.yaml", "tsconfig.base.json", ".env.example"],
"globalEnv": ["NODE_ENV"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**", "out/**"]
},
"lint": {
"dependsOn": ["^build"],
"outputs": []
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
outputs 是可以从缓存恢复的产物。写少了,构建虽然成功但缓存没有价值;写多了,又会把临时文件和框架内部缓存带进 CI。对于 Next.js,排除 .next/cache/** 通常更稳。
如果某个应用有特殊输出,可以在 package 内放 turbo.json。数组字段默认会替换根配置;想追加时,把 $TURBO_EXTENDS$ 放在数组第一项。
{
"extends": ["//"],
"tasks": {
"build": {
"outputs": ["$TURBO_EXTENDS$", ".next/**", "!.next/cache/**"],
"env": ["NEXT_PUBLIC_API_URL"]
}
}
}
CI 和验证命令要固定
Turborepo 在 CI 中最容易体现价值,但 --affected 需要完整的 Git 比较历史。GitHub Actions 里建议设置 fetch-depth: 0,否则浅克隆可能让所有 package 都被判定为变更。
name: turbo-ci
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 11.5.1
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run lint type-check test build --affected
- run: pnpm turbo run build --dry=json > turbo-plan.json
- uses: actions/upload-artifact@v4
if: always()
with:
name: turbo-plan
path: turbo-plan.json
使用 Remote Cache 时,本地先执行 pnpm dlx turbo login 和 pnpm dlx turbo link,CI 中通过 secret 提供 TURBO_TOKEN 和 TURBO_TEAM。同时要记住,日志也可能成为缓存产物,所以构建脚本不要打印 API key、用户数据或 session token。
把常用命令写进 CLAUDE.md,Claude Code 就不需要每次重新猜。
pnpm turbo run build --dry=json
pnpm turbo run build --filter=@acme/web...
pnpm turbo run test --filter=...[origin/main]
pnpm turbo run lint --filter=!./apps/docs
pnpm turbo run build --cache=local:rw,remote:r
pnpm turbo run build --force
给 Claude Code 的提示词模板
不要只说“帮我配置 Turborepo”。要把边界、禁止事项和验证命令一次说清楚。
这个仓库是 Turborepo v2 + pnpm workspace monorepo。
边界:
- apps/* 是可部署应用。
- packages/ui 只放视觉组件。
- packages/utils 只放与框架无关的函数。
- packages/* 不能依赖 apps/*。
- turbo.json 必须使用 tasks,不要使用 pipeline。
任务:
1. 读取 package.json 和 turbo.json,解释任务依赖图。
2. 指出缓存 outputs 缺失或可疑的地方。
3. 如需修改,请用最小安全差异。
4. 最后运行这些命令并报告结果:
pnpm turbo run lint type-check test build --affected
pnpm turbo run build --dry=json
这个模板能减少 AI 常见的大范围重写。它也能阻止一些危险提案,比如在 packages/ui 写 HTTP 调用、在 packages/utils 里依赖 Next.js、或者把全仓库构建当成唯一 CI 策略。
常见坑
第一,复制旧的 pipeline 示例。Turborepo v2 使用 tasks。迁移旧仓库时,可以让 Claude Code 对照官方文档解释每一个变更。
第二,缓存范围过大。不要把 node_modules/**、临时日志或不可部署的内部缓存写进 outputs。缓存构建产物,不要缓存整个工作区。
第三,在浅克隆中使用 --affected。如果 CI 没有 base 和 head 的历史,Turborepo 无法计算正确范围,结果可能退化成全量任务。
第四,一次让 Claude Code 做太多事。先引入 Turborepo,再整理 root scripts,最后接入 CI。不要把 package 拆分、ESLint 升级和依赖升级混在同一个请求里。
第五,过度共用。巨大的 packages/shared 会让每次变更都像全局变更。ui、utils、contracts、tsconfig 这种小而明确的 package 更容易维护。
收益导线和下一步
Turborepo 不只是加速工具,它也能保护收入路径。内容站可以在构建失败时停止发布,SaaS 可以在同一个流程中验证价格页、注册、账户设置和管理功能。团队开发中,CI 缩短也会减少 review 等待时间。
ClaudeCodeLab 可以帮助团队把这些规则落成可重复流程,包括 CLAUDE.md、package 边界、CI 命令、review prompt 和发布规则。自学可以继续读 monorepo 管理 与 CI/CD 设置。团队落地可以从 training / consultation 开始。
实际试用结果
Masa 把这套方式应用到两个 Vite 应用和一个共享 UI package 的小型仓库中。第一次运行会执行所有任务,之后 build 和 type-check 能看到缓存命中。最初的问题是 outputs 太宽,把框架内部缓存也存进了 CI artifact,导致结果很吵。最终保留下来的就是本文这种方式:v2 tasks、收窄的 outputs、受影响范围验证,以及先审查边界再改文件的 Claude Code 提示词。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。