Claude CodeとTurborepoで高速モノレポ開発を実現する実践ガイド
Claude CodeでTurborepoモノレポを設計し、tasks、キャッシュ、CI、落とし穴まで実例コードで解説。
Claude CodeとTurborepoを組み合わせる理由
モノレポは、複数のアプリや共通パッケージを1つのリポジトリで管理する開発方式です。最初は便利ですが、apps/web、apps/admin、packages/ui、packages/utils のように境界が増えると、ビルド順序、依存関係、CI時間、キャッシュの扱いでつまずきます。
Turborepoは、その問題を「タスクの依存グラフ」と「キャッシュ」で整理するビルドシステムです。Claude Codeは、単に設定ファイルを生成する道具ではなく、既存リポジトリを読み、パッケージ境界を説明し、危ない変更範囲をレビューするペア開発者として使うと効果が出ます。
この記事では、2026年6月2日時点のTurborepo v2系を前提にします。公式では turbo.json のタスク定義は pipeline ではなく tasks を使います。一次情報は Turborepo configuration、turbo run、Remote Caching、Claude Code側は Claude Code memory を確認してください。
関連する全体像は、Claude Codeモノレポ管理、Claude Codeとpnpm workspace、Claude Code CI/CD設定ガイド と合わせて読むとつながります。
完成形と3つのユースケース
この記事の完成形は、pnpm workspace上にTurborepoを載せた小さなTypeScriptモノレポです。巨大な企業リポジトリをいきなり想定せず、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
実例は3つあります。1つ目は、顧客向けWebアプリと管理画面で同じボタン、フォーム、入力バリデーションを使うケースです。packages/ui と packages/utils を分けると、Claude Codeが「見た目の部品」と「業務ロジック」を混ぜにくくなります。
2つ目は、LP、ドキュメント、管理画面を同じチームで育てるケースです。毎回すべてをビルドするとCIが重くなるため、--affected や --filter で変更影響のある範囲だけを検証します。
3つ目は、記事サイトやSaaSのように収益導線を持つプロダクトです。公開前に lint、type-check、test、build を固定化しておくと、Claude CodeがCTAや価格ページ周辺を触ったときも、壊れた導線をCIで止められます。
最小構成をコピーして作る
まずはリポジトリの形を固定します。ポイントは、アプリと共有パッケージを分けすぎないことです。最初から packages/core、packages/common、packages/shared を乱立させると、何をどこに置くべきか人間にも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 でしたが、実プロジェクトではロックファイルで固定してください。
{
"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"
}
}
共有パッケージは、内部依存を workspace:* で書きます。これは「npm registryではなく、このworkspace内のパッケージを使う」という意味です。
{
"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を明示する
Turborepo v2系の中心は turbo.json です。tasks の各キーは、各パッケージの package.json にある同名スクリプトを探して実行します。dependsOn の ^build は「依存先パッケージのbuildを先に終わらせる」という意味です。
{
"$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 はキャッシュ対象の成果物です。ここが抜けると、ビルドは通っても次回のキャッシュヒットが期待通りになりません。逆に .next/cache/** のようなフレームワーク内部キャッシュまで保存すると、キャッシュが肥大化し、古い結果を引き戻す原因になります。
Next.jsやAstroのようにアプリごとに成果物が違う場合は、パッケージ内の 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 を指定し、比較に必要な履歴を取ります。
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には TURBO_TOKEN と TURBO_TEAM をsecretで渡します。ログもキャッシュ成果物として扱われるため、APIキーや顧客情報を console.log しない運用も同時に決めてください。
日常的に使う検証コマンドは、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への依頼テンプレ
Claude Codeには「Turborepoを設定して」とだけ言うより、境界、禁止事項、検証コマンドをまとめて渡します。これはエージェントの足場、つまり作業の前提条件を固定するためです。
このリポジトリはTurborepo v2 + pnpm workspaceのモノレポです。
守る境界:
- apps/* はデプロイ対象のアプリです。
- packages/ui は見た目の部品だけを持ちます。
- packages/utils はフレームワーク非依存の関数だけを持ちます。
- packages/* から apps/* へ依存してはいけません。
- turbo.json は pipeline ではなく tasks を使ってください。
今回の依頼:
1. package.json と turbo.json を読んで、タスク依存関係を説明してください。
2. キャッシュ対象の outputs が不足している箇所を指摘してください。
3. 変更が必要な場合は、最小差分で修正してください。
4. 最後に次のコマンドを実行して結果を報告してください。
pnpm turbo run lint type-check test build --affected
pnpm turbo run build --dry=json
このテンプレートの価値は、Claude Codeの出力を「設定ファイルの作成」ではなく「境界を守ったレビュー」に寄せられることです。特に、packages/ui にAPI通信を入れる、packages/utils からNext.jsへ依存する、CIで全件ビルドだけを回す、といった雑な提案を防ぎやすくなります。
具体的な落とし穴
1つ目の落とし穴は、古い記事や既存設定から pipeline をコピーすることです。Turborepo v2系では tasks を使います。移行中のリポジトリでは、Claude Codeに「v1の設定をv2へ変換し、公式ドキュメントの差分を説明して」と頼むと安全です。
2つ目は、outputs を広くしすぎることです。node_modules/**、.next/cache/**、一時ログを入れると、キャッシュが重くなり、復元結果も読みにくくなります。成果物だけを保存し、ログに秘密情報を出さない運用を徹底してください。
3つ目は、--affected を浅いcheckoutで使うことです。CIで履歴が足りないと、Turborepoは変更範囲を正しく計算できず、全パッケージが対象になることがあります。GitHub Actionsでは fetch-depth: 0 を基本にします。
4つ目は、Claude Codeに大きすぎる変更を一度に依頼することです。「Turborepo導入」「パッケージ分割」「ESLint更新」「CI移行」を同時に頼むと、失敗時に原因が追えません。最初は turbo.json、次にroot scripts、最後にCIという順番で分けます。
5つ目は、共通化のしすぎです。packages/shared に何でも入れると、少しの修正で全アプリが影響を受けます。共通化する理由を「2つ以上のアプリで同じ責務がある」まで絞ると、TurborepoのグラフもClaude Codeのレビューも読みやすくなります。
収益導線と次の一手
Turborepoの導入は、開発速度だけでなく公開品質にも効きます。記事サイトならビルド失敗で公開を止められます。SaaSなら価格ページ、登録フォーム、管理画面の変更を同じPRで検証できます。受託やチーム開発なら、CI時間の短縮がそのままレビュー待ち時間の短縮になります。
ClaudeCodeLabでは、こうしたモノレポ設計、CLAUDE.md の整備、CI/CDの検証コマンド化をチーム単位で整える相談も扱っています。自分で進める場合は Claude Codeモノレポ管理 と CI/CD設定ガイド を読み、チーム導入を急ぐ場合は training / consultation から相談してください。
この記事で紹介した内容を実際に試した結果
Masaが小規模なViteアプリ2つと共有UIパッケージ1つの構成にこの設定を当てたところ、初回は通常どおり全タスクが走り、2回目以降は build と type-check のキャッシュヒットを確認できました。一方で、最初に .next/cache/** 相当の内部キャッシュまで outputs に含めたときは成果物が膨らみ、CIログの確認も遅くなりました。最終的には、tasks、最小限の outputs、--affected、Claude Codeへの依頼テンプレを分けて整える方法が、一番説明しやすく壊れにくい運用でした。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
ObsidianメモをCLAUDE.mdに変えるClaude Code運用: 文脈を毎回説明しない仕組み
Obsidianの作業メモからCLAUDE.md用の運用ノートを作り、Claude Codeに安定した文脈を渡す方法。
Claude Code Revenue CTA Routing: 記事からPDF、Gumroad、相談へ送る設計
PVだけで終わらせず、読者の状態に合わせて無料PDF、Gumroad教材、導入相談へ分岐するCTA設計です。
Claude Codeチーム引き継ぎルール: レビュー、権限、収益導線まで渡す実務手順
Claude Codeの作業をチームで渡すための証拠、権限、ロールバック、無料PDF/Gumroad/相談導線の実務ルール。