Advanced (更新: 2026/6/1)

Claude Codeでモノレポ管理を標準化する実践ガイド

Claude Codeでモノレポを安全に運用する実践ガイド。pnpm、Turborepo/Nx、CODEOWNERS、CIまで具体例で解説。

Claude Codeでモノレポ管理を標準化する実践ガイド

モノレポとは、複数のアプリやライブラリを1つのGitリポジトリで管理する構成です。apps/webapps/apipackages/uipackages/shared のように分けると、型、UI、設定、CIを横断して再利用できます。一方で、境界を決めないままClaude Codeに「いい感じに直して」と頼むと、依存関係が絡まり、CIが重くなり、レビュー担当も曖昧になります。

この記事では、Claude Codeをモノレポの「作業者」ではなく「構造を守る相棒」として使う手順をまとめます。実務で効く順番は、先にリポジトリ地図を作り、パッケージ境界を決め、pnpm workspaceで依存を固定し、TurborepoまたはNxのaffected tasks(変更の影響がある範囲だけ実行する仕組み)でCIを絞ることです。

公式概念は、Nxのモノレポ判断基準Nx affectedNx mental modelpnpmTurborepo docsを土台にしています。Claude Code側のコツは、これらの用語をプロンプトに入れ、出力後に境界ルールとCIで検証させることです。

私がMasaとして記事サイト、教材ページ、管理画面、検索まわりを同じリポジトリで扱ったときに一番困ったのは、ツール選定そのものではありませんでした。問題は「この変更はどこまで影響するのか」を毎回説明できないことでした。Claude Codeは広い範囲を読めるので便利ですが、境界が曖昧なリポジトリでは、便利さの分だけ変更範囲も広がります。そこで、この記事の方針は「Claude Codeに賢く推測させる」ではなく「推測しなくてよい状態を作る」です。

モノレポにする前に決めること

モノレポは、すべてのプロジェクトに向く万能解ではありません。小さなLPと小さなAPIを同じ人が管理しているだけなら、単一リポジトリを分けても大きな問題は起きません。逆に、共通UI、共通型、共通認証、共通CIを何度もコピーしているなら、モノレポにする価値があります。

判断基準は3つです。1つ目は、同じ変更を複数リポジトリに反映しているか。2つ目は、パッケージ間の互換性を同じPRで確認したいか。3つ目は、チームごとのレビュー責任をパス単位で分けられるかです。この3つに答えられないまま移行すると、モノレポは「便利な統合」ではなく「巨大な未整理フォルダ」になります。

Claude Codeに相談する場合も、最初の質問は「モノレポを作って」ではなく「この構成はモノレポにする価値があるか、分けたままにすべきか」です。既存のアプリ数、共有したいコード、デプロイ単位、担当チーム、CI時間を渡すと、Claude Codeは移行すべき範囲とまだ分けておくべき範囲を整理しやすくなります。

実装の順番を固定する

モノレポ導入で失敗しやすいのは、いきなり共通パッケージを作ることです。最初にpackages/sharedを作ると、目についた重複を何でも移したくなります。しかし、共通化の前に必要なのは、現在の重複が「本当に同じ概念」なのかを確認することです。名前が同じUserでも、管理画面のユーザー、請求先のユーザー、認証セッションのユーザーは別物かもしれません。

おすすめの順番は、リポジトリマップ、依存ポリシー、CI、共通化の順です。まずClaude Codeに既存構造を読ませ、次に依存してよい方向を決めます。そのあと、check:depsci:affectedを入れて、境界違反を検出できるようにします。最後に、共通化しても壊れにくい型、純粋関数、UIプリミティブだけを移します。

この順番にすると、Claude Codeの出力も変わります。「重複しているのでsharedへ移動しました」ではなく、「この関数は副作用がなく、apps/apiへ依存していないためsharedへ移動できます」のように、理由付きの提案になります。AdSense向けの記事改善でも、同じ構造が効きます。公開記事、検索、商品ページ、相談フォームを同じリポジトリで扱う場合、収益導線に関わる変更ほど影響範囲を説明できる状態が必要です。

全体像

モノレポ管理で最初に作るべきものは、コードではなく地図です。地図なしで共通化を始めると、packages/shared が何でも置き場になり、数か月後に削れない依存が増えます。

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/* は再利用する部品です。パッケージ境界とは「どのパッケージがどのパッケージに依存してよいか」を明文化した線です。Claude Codeには、この線を越えた変更を提案させないようにします。

Claude Codeに最初に読ませるリポジトリマップ

初回プロンプトでは、作業依頼より先に「読んでほしい範囲」と「守る境界」を渡します。以下はそのまま貼れる依頼文です。

このリポジトリをモノレポとして把握してください。

前提:
- apps/web は Next.js の画面
- apps/api は API サーバー
- packages/ui は画面部品
- packages/shared は型、バリデーション、純粋関数
- packages/config は ESLint、TypeScript、Prettier、Vitest などの共通設定

守るルール:
- apps/* から apps/* へ直接依存しない
- packages/* から apps/* へ依存しない
- 内部パッケージの依存は workspace:* を使う
- 変更後は affected tasks で lint/test/build を確認する

最初に、依存関係、危ない循環依存、共通化しすぎているファイル、CIで確認すべきコマンドを一覧化してください。
まだ編集はしないでください。

ここで「まだ編集はしない」と入れるのが重要です。Claude Codeは広い文脈を読める反面、最初から修正まで任せると、既存設計を理解する前に共通化を始めることがあります。リポジトリマップを先に出させると、レビュー対象が「提案」になり、失敗してもGit差分が汚れません。

pnpm workspaceで依存の入口を固定する

pnpm workspaceは、複数パッケージを同じリポジトリ内で扱うための仕組みです。内部パッケージはworkspace:*で参照します。これにより、npmレジストリ上の同名パッケージを誤って解決する事故を避けやすくなります。

packages:
  - "apps/*"
  - "packages/*"

ルートのpackage.jsonには、人間もClaude Codeも迷わないスクリプト名を置きます。

{
  "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"
  }
}

各アプリの内部依存は次のように書きます。

{
  "dependencies": {
    "@acme/shared": "workspace:*",
    "@acme/ui": "workspace:*"
  }
}

Claude Codeへの依頼は「依存を追加して」だけでは弱いです。次のように言うと、設定ファイル、import、CIまで一貫して確認しやすくなります。

apps/web から @acme/ui と @acme/shared を使えるようにしてください。
package.json では workspace:* を使い、直接相対パス import は避けてください。
変更後に pnpm install --lockfile-only、pnpm check:deps、pnpm ci:affected で確認できる形にしてください。

TurborepoとNx affected tasksを使い分ける

Turborepoは、package scriptsを高速に並列実行し、キャッシュする用途に向いています。小中規模のTypeScriptモノレポなら、まずTurborepoで十分です。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 workspaceに後から入れるなら、まず初期化してからaffectedを使います。

pnpm dlx nx@latest init
pnpm nx affected -t lint test build --base=origin/main --head=HEAD

どちらを選んでも、Claude Codeへの指示は「全件ビルドして」ではなく「変更影響範囲だけ確認して」に寄せます。大規模リポジトリで毎回全パッケージを走らせると、CI時間も料金も膨らみます。AdSense向けのサイト運用でも、記事生成、検索、決済、管理画面が同じリポジトリにあるなら、この差はそのまま公開速度に効きます。

CODEOWNERSでレビュー責任を固定する

CODEOWNERSは、GitHubで特定パスのレビュー担当を自動指定するファイルです。Claude Codeがよい差分を作っても、レビュー担当が曖昧ならモノレポは崩れます。

/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

この設定をCODEOWNERSとして置いたうえで、Claude Codeには「packages/sharedを触る場合は影響するappsを列挙して」と頼みます。共通パッケージは便利ですが、変更範囲が広いため、レビューとテストの密度を上げるべき場所です。

依存ポリシーをコードで検査する

口約束の境界ルールはすぐ破られます。次のスクリプトは、内部パッケージがworkspace:で参照されているか、apps/*への依存が混ざっていないかを確認します。scripts/check-workspace-deps.cjsとして保存すれば、node 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.`);

Claude Codeにこのスクリプトを追加させる場合は、「検査コードもテスト対象」と明記します。たとえば、あえて"@acme/api": "1.0.0"を入れたサンプルで失敗することを確認させると、単なる飾りスクリプトで終わりません。

CIチェックリストを最小化する

モノレポのCIは、全部やるほど安全に見えますが、毎回30分かかるCIは誰も見なくなります。まずは以下のGitHub Actionsから始めます。

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は重要です。Turborepoの--affectedやNx affectedはGitの差分を見ます。履歴が浅すぎると、変更範囲を正しく絞れず、全件扱いになったり、逆に必要なタスクが漏れたりします。

実務ユースケース

1つ目は、packages/uiのButtonを変更するケースです。Claude Codeには「@acme/uiの公開APIを変えずにButtonのloading状態を追加し、apps/webの利用箇所をaffected範囲で確認して」と依頼します。UIパッケージの変更は見た目だけに見えて、フォーム、管理画面、LPのCTAに影響します。PRにはスクリーンショットとpnpm ci:affectedの結果を添えます。

2つ目は、認証ユーザー型をpackages/sharedに移すケースです。APIとWebで同じUserRoleを使えるので重複は減りますが、共通型を肥大化させると逆に変更しにくくなります。Claude Codeには「DBモデルではなく公開DTOだけをsharedに置く」と指定します。DTOは、APIレスポンスや画面入力で使う安全なデータ形です。

3つ目は、Next.jsやTypeScriptのアップグレードです。モノレポでは一部だけ上げると、tsconfig、ESLint、テスト環境がずれます。Claude Codeには「packages/configから順に更新し、apps/webとapps/apiの差分を分けてPR説明を書いて」と頼みます。依存更新はpnpm -r updateだけで終わらせず、lockfile、型チェック、affected buildまで確認します。

4つ目は、チーム横断の機能追加です。たとえば請求機能を入れる場合、UI、API、shared schema、監査ログが同時に動きます。Claude Codeに「作業を4つのPRに分ける案」を出させると、巨大PRを避けられます。モノレポはまとめて編集できることが利点ですが、まとめてレビューする必要はありません。

よくある失敗と落とし穴

最も多い失敗は、packages/sharedを万能箱にすることです。formatDateUserRoleはよくても、DB接続、画面固有hooks、APIクライアントまで入れると、全パッケージがsharedに引きずられます。Claude Codeには「sharedに置く理由と置かない理由を説明して」と毎回確認させます。

次に危ないのは、相対パスで境界を越えるimportです。../../packages/shared/srcのようなimportは短期的には動きますが、ビルド順序、型解決、公開APIのレビューを壊します。内部パッケージは@acme/sharedのような名前でimportし、workspace:*で依存を宣言します。

3つ目は、TurborepoとNxを同時に深く入れすぎることです。どちらも強力ですが、同じタスクを二重に管理すると、CIが読みにくくなります。まずTurborepoでpackage scriptsを整理し、プロジェクトグラフやaffectedの精度がもっと必要になったらNxを検討する、という順番が現実的です。

4つ目は、Claude Codeの出力を「動いたからOK」にすることです。モノレポでは、動いた1アプリの裏で別アプリが壊れていることがあります。PR説明には、変更したパッケージ、影響するアプリ、実行したaffected tasks、未確認の理由を書かせてください。

Claude Code向けのレビュー依頼テンプレート

最後に、PR前の確認で使える依頼文です。

今回の差分をモノレポ観点でレビューしてください。

確認してほしいこと:
- apps/* から apps/* への直接依存がないか
- packages/* から apps/* への依存がないか
- 内部依存が workspace:* になっているか
- packages/shared に置くべきでない実装が混ざっていないか
- Turborepo または Nx affected で確認すべきタスクが足りているか
- CODEOWNERS上のレビュー担当が明確か

出力形式:
- ブロッカー
- 修正推奨
- 確認済みコマンド
- PR本文に書くべき影響範囲

このテンプレートは、Claude CodeとNx workspaceClaude Codeとpnpm workspaceClaude CodeとTurborepoチーム開発のClaude Code運用と合わせて読むと実務に落とし込みやすいです。

チームでClaude Codeをモノレポに導入する場合、技術選定より先に「誰がどの境界を守るか」を決める必要があります。ClaudeCodeLabでは、CLAUDE.md、CODEOWNERS、CI、レビュー依頼テンプレートまで含めたClaude Code研修・導入相談を扱っています。既存リポジトリを前提に、最初の安全なPR単位まで一緒に分解できます。

まとめ

Claude Codeでモノレポ管理を成功させる鍵は、生成力ではなく制約です。リポジトリマップ、package boundaries、pnpm workspace、Turborepo/Nx affected tasks、CODEOWNERS、依存ポリシー、CIチェックリストを先に置くと、Claude Codeの提案は実務でレビュー可能な差分になります。

この記事で紹介した内容を実際に試した結果、最も効果が大きかったのはworkspace:*の強制とpnpm ci:affectedの標準化でした。特にpackages/sharedの変更時に影響アプリをClaude Codeへ列挙させる運用にしただけで、レビュー漏れと不要な全件CIが減り、記事サイトやSaaS管理画面を同じリポジトリで扱うときの安心感が大きく上がりました。

#Claude Code #モノレポ #pnpm workspace #Turborepo #Nx
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。