// AI長文・約 2613,804

大規模Monorepo × Claude Codeで実現する開発体験の向上

大規模Monorepo × Claude Codeで実現する開発体験の向上

3つの大規模プロジェクトを抱えるMonorepoに、Claude Codeのフック・Agent・Skillsを統合し、開発体験を劇的に改善した実践例を紹介します。Turborepoとの組み合わせで実現した品質担保と効率化の工夫をご覧ください。

// この記事のポイント

  • 技術スタックの異なる 3 プロジェクトを 1 つの Monorepo で共存させるための名前空間設計と共有レイヤー最小化の方針
  • Turborepo の インクリメンタルビルド × 並列実行で、影響範囲を絞ったテスト・ビルドを実現
  • Claude Code の フック / Agent / Skills をプロジェクト固有のドメイン知識と組み合わせて、レビュー前の機械的指摘を自動化
  • このプロジェクトでは結果として PR レビューコメントが 平均 8〜10 件 → 2〜3 件に減少し、本質的な議論に時間を使えるように

はじめに

私たちが管理するMonorepoは、技術スタックの異なる3つのプロジェクトが共存しています。ECサイトの在庫管理(時系列DB)、顧客データ分析(MongoDB)、IoTセンサー処理(PostgreSQL)それぞれ異なるUIライブラリを使い、ドメイン知識も独立しています。

この構成で最初につまずいたのは、「共通パッケージを1行変えたら、どのプロジェクトのテストを実行すべきか分からない」という問題でした。影響範囲の特定に時間を取られ、念のため全体をビルドする運用になっていました。さらに、AIアシスタントを使い始めると別の課題が浮上しました。3つのプロジェクトのドメイン知識を毎回説明しなければならず、長時間のセッションでコンテキストが枯渇する場面が増えてきたのです。

本記事では、TurborepoとClaude Codeのフック・Agent・Skillsを組み合わせることで、これらの問題をどう解決したかを紹介します。

Monorepo構成の設計思想

3つの独立プロジェクトの共存

「全てを統一すべき」──Monorepoを設計する際、最初はそう考えていました。しかし、実際にはビジネス要件や技術的制約により、プロジェクトごとに異なる技術選択が必要になります。

  • あるプロジェクトでは大量のリアルタイムデータ処理が必須のため、時系列DB対応のデータベースが不可欠
  • 別のプロジェクトではドキュメント指向のデータモデルが最適で、MongoDBを採用
  • UIデザインシステムも、顧客の既存ブランドガイドラインに合わせて複数のライブラリを使い分ける必要がありました

一見すると非効率に思えるかもしれません。しかし、適切な名前空間設計共有レイヤーの最小化を意識することで、むしろ各プロジェクトが最適な技術選択をしながら、必要な部分だけを共有できる柔軟な構成を実現できました。

名前空間による明確な分離

# ワークスペース構成
workspaces/
├── @repo/*              # 全プロジェクト共有パッケージ
├── @project-a/*         # Project A専用パッケージ
├── @project-b/*         # Project B専用パッケージ
└── @project-c/*         # Project C専用パッケージ

この名前空間により、以下のメリットが得られます。

  1. 誤った依存関係を防止: TypeScriptがコンパイル時に検出
  2. プロジェクト固有の最適化: 各プロジェクトが独自の技術選択を維持
  3. 明示的な依存関係: @repo/*のみが共通、それ以外は独立

共有 vs 個別化の判断基準

Monorepoで最も悩ましいのが、「何を共有し、何を個別化するか」という判断です。私たちは以下の基準で判断しました。

共有化の判断基準: 「プロジェクト間で一貫性を保つべきもの」

1. TypeScript設定(@repo/typescript-config

チーム全体で同じ型チェックレベルを維持するため、基本的なTypeScript設定は共有化しました。strict: trueisolatedModules: trueといった設定を全プロジェクトで統一することで、「Project Aでは型エラーが出ないのに、Project Bでは出る」といった混乱を避けられます。

ただし、各プロジェクトのtsconfig.jsonでは、パスエイリアスなどプロジェクト固有の設定をextendsで上書きできるようにしています。

2. ESLint設定(@repo/eslint-config

コードスタイルの一貫性は、複数プロジェクトを横断して作業する際に重要です。共通のルールセットを持つことで、「どのプロジェクトでも同じ書き方」ができ、コードレビュー時の認知負荷が下がります。

3. UIコンポーネント(@repo/ui - Atomic Design)

Button、Input、Modalなどの基本的なUIコンポーネントは、全プロジェクトで再利用できるよう共有化しました。Atomic Designに基づいた階層構造(Atoms, Molecules, Organisms)で整理し、Storybookでドキュメント化しています。

ただし、プロジェクト固有のUIライブラリ(Ant Design、Material-UI)の上に薄いラッパーを作る形で実装しているため、各プロジェクトの見た目の違いは保ちつつ、ロジックは共通化できています。

4. AWS SDK ラッパー(@repo/aws

S3へのアップロード、Lambda関数の呼び出しなど、AWS SDKの基本的な操作は全プロジェクトで共通です。エラーハンドリングやリトライロジックを共通化することで、車輪の再発明を避けられました。

5. インフラストラクチャコード(@repo/infrastructure

AWS CDKのConstruct(VPC、ALB、RDSなど)を共通化し、インフラの構成パターンを統一しています。これにより、新しいプロジェクトを立ち上げる際の初期セットアップ工数が減りました。


個別化の判断基準: 「プロジェクト固有の要件があり、共通化すると制約になるもの」

1. データベース層

これは最も悩んだ部分です。当初は「全プロジェクトでPostgreSQLに統一しよう」と考えていました。しかし、大量のリアルタイムデータを扱うプロジェクトでは時系列DBが必須、ドキュメント指向のデータ構造が最適なプロジェクトではMongoDBが適している、といった要件の違いが明確になり、個別化を選択しました。

データベース層を個別化したことで、各プロジェクトが最適な技術選択をしながら、クエリビルダー(Knex.js)やPrismaなどの共通ツールは@repo/db-utilsで共有する、というバランスを取れました。

2. AWSリソース設定

Lambda関数名(project-a-processorproject-b-analyzerなど)、S3バケット名、IAMロールは、プロジェクトごとに完全に独立させる必要があります。これらを共有すると、デプロイ時の競合や、意図しないリソースへのアクセスが発生するリスクがあります。

各プロジェクトの@project-a/infrastructure@project-b/infrastructureで個別に管理し、共通のCDK Constructのみを@repo/infrastructureから利用する形にしました。

3. 外部API連携

決済サービス(Stripe、PayPal)、地図サービス(Mapbox、Google Maps)、通知サービス(Twilio、SendGrid)など、プロジェクトごとに異なるサードパーティサービスを利用しています。顧客要件や既存契約の関係で、統一できない部分です。

各プロジェクトの@project-a/external-api@project-b/external-apiで個別に管理しています。

4. HTTPスキーマ(tRPC)

API設計はビジネスロジックに直結するため、プロジェクト固有のスキーマが必要です。@project-a/http-schema@project-b/http-schemaで個別に管理し、tRPCによる型安全性を各プロジェクトで享受しています。


判断のポイント:

  • 「プロジェクト間で一貫性が必要か?」→ Yes なら共有化
  • 「プロジェクト固有の要件があるか?」→ Yes なら個別化
  • 「共通化すると制約になるか?」→ Yes なら個別化
  • 「重複を許容できるか?」→ No なら共有化を検討

この判断基準により、柔軟性と一貫性のバランスを取れるMonorepo構成を実現できました。

Turborepoによる効率化

Monorepoの最大の課題の1つが「ビルド時間の増大」です。プロジェクトが増えるにつれ、全体をビルドするのに時間がかかり、開発のテンポが落ちていきます。私たちもこの問題に直面し、Turborepoを導入することで解決しました。

Turborepoとは

Turborepoは、Monorepo向けの高速ビルドシステムです。「変更されたパッケージのみをビルド」「依存関係を自動解析して並列実行」「ビルド結果をキャッシュ」という3つの機能により、ビルド時間を劇的に短縮します。

導入前の課題

導入前は、npm workspacesのみを使用していました。

問題点:

  • 小さな変更でも、全パッケージをビルドする必要があった(5分)
  • 依存関係の順序を手動で管理する必要があった
  • CI/CDで毎回同じビルドを繰り返していた(10分)

特に、Lambda関数を1つだけ修正したのに、「念のため全体をビルド」してしまうことが多く、無駄な待ち時間が発生していました。

Turborepoの設定

// turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],  // 依存パッケージを先にビルド
      "outputs": ["dist/<strong>", ".next/</strong>"],  // キャッシュ対象
      "inputs": ["src/**", "package.json", "tsconfig*.json"]  // 変更検知対象
    },
    "test": {
      "dependsOn": ["^build"],  // ビルド完了後にテスト
      "cache": true
    }
  },
  "globalDependencies": [".npmrc", "package-lock.json", "turbo.json"]
}

設定のポイント:

  • dependsOn: ["^build"]: 依存パッケージを先にビルドする順序保証
  • outputs: ビルド成果物をキャッシュ対象として指定
  • inputs: これらのファイルが変更されたらキャッシュを無効化
  • globalDependencies: これらが変更されたら全キャッシュを無効化

依存グラフの自動解析

Turborepoは、package.jsonの依存関係を自動解析し、最適なビルド順序を決定します。

: @project-a/app@repo/uiに依存している場合

1. @repo/ui をビルド
2. @project-a/app をビルド(@repo/uiのビルド完了後)

これを手動で管理する必要はありません。package.jsonの依存関係を読み取り、Turborepoが正しいビルド順序を決定します。

インクリメンタルビルドの効果

変更前: 全パッケージをビルド

npm run build
 全パッケージをビルド

変更後: 変更されたパッケージのみビルド

# Lambda関数1つだけ修正した場合
npm run build:project-a-lambda-processor
 Lambda関数のみビルド(数秒)
 
# 共通UIコンポーネントを修正した場合
npm run build
 @repo/ui + 依存する3つのアプリをビルド

効果: Lambda関数1つの変更なら数秒、共有UIコンポーネントの変更でも関連3プロジェクト分のビルドで済みます。

並列実行による高速化

Turborepoは、依存関係のないタスクを並列実行します。

: Project A、B、Cのビルド(互いに依存なし)

# 順次実行の場合(導入前)
Project A Project B Project C
全プロジェクトを順番に実行
 
# 並列実行の場合(Turborepo)
Project A、B、C を同時実行
最も遅いプロジェクトの完了を待つだけ

CPUコアを使い切ることで、私たちの環境では3プロジェクト並列のビルドでも直列実行の約3分の1の時間で完了します。

キャッシュ戦略

Turborepoは、ビルド結果をキャッシュし、同じ入力なら再ビルドをスキップします。

ローカルキャッシュ:

# 1回目: ビルド実行
npm run build:project-a
 
# 2回目: キャッシュから復元(瞬時)
npm run build:project-a
 Cache hit! ビルドをスキップ

キャッシュ無効化の条件:

  • inputsで指定したファイルが変更された
  • globalDependenciesが変更された
  • 依存パッケージが変更された

ブランチを切り替えて戻ってきた際、以前のビルド結果がキャッシュから復元されるため、再ビルドの待ち時間がゼロになりました。

実行例と効果

1. プロジェクト単位のビルド:

npm run build:project-a
 turbo build --filter @project-a/* --filter @project-a-repo/*
 Project A関連のみビルド

2. Lambda関数単体のビルド:

npm run build:project-a-lambda-processor
 turbo build --filter @project-a/lambda-processor
 Lambda関数のみビルド(高速)

3. 共有パッケージのビルド:

npm run build:packages
 turbo build --filter '@repo/*'
 共有パッケージのみビルド

4. ウォッチモード:

npm run watch-build:packages
 turbo build --filter '@repo/*' --watch
 ファイル変更を検知して自動再ビルド

CI/CDでの効果

導入前: 毎回全体をビルド

導入後: キャッシュを活用

  • 変更されたパッケージのみビルド
  • 未変更のパッケージはキャッシュから復元
  • 並列実行で高速化

効果: 私たちのプロジェクトでは全体ビルド(毎回10分)が、変更箇所のみビルド+キャッシュ復元の組み合わせで2〜3分に収まるようになり、PRへのフィードバックが返るまでの時間が短縮されました。


Turborepoのメリット:

  • ビルド待ち時間の削減: インクリメンタルビルドとキャッシュの組み合わせ
  • CI/CDの高速化: 変更部分のみビルド、キャッシュ活用
  • 依存関係の自動解析: 手動でのビルド順序管理が不要
  • スケーラビリティ: プロジェクトが増えてもビルド時間が線形増加しない

Turborepoの導入により、Monorepoの「ビルドが遅い」という最大のデメリットを克服できました。

Monorepo開発におけるコンテキスト管理の課題

Turborepoでビルド時間の問題は解決しましたが、Monorepoならではの別の課題が浮上してきました。それは、AIアシスタントを使った開発におけるコンテキスト管理です。

複雑さが生む情報量の問題

3つの独立したプロジェクトを抱えるMonorepoでは、以下のような情報を扱う必要があります。

  • 各プロジェクトのドメイン知識(用語、ビジネスロジック)
  • プロジェクトごとに異なる技術スタック(DB、UIライブラリ)
  • 共有パッケージと個別パッケージの依存関係
  • コーディング規約、命名規則、ディレクトリ構造
  • よく使うコマンドやビルド手順

これらすべてを一度に読み込むと、AIアシスタントのコンテキストウィンドウを圧迫してしまいます。

長時間セッションでの課題

特に大規模な機能開発や品質チェック時に、以下のような問題が発生しがちです。

  • コンテキストの枯渇: 長時間の作業で過去の文脈が失われる
  • 情報の混在: 複数プロジェクトの情報が混ざり、意図しない提案が生成される
  • 重複読み込み: 同じルールやドメイン知識を何度も読み込む非効率さ

解決の方向性

これらの課題を解決するため、私たちは以下の戦略を採用しました。

  1. Agent間の責務分離: 各Agentが特定の目的のみを持ち、必要最小限の情報だけを読み込む
  2. Skillsによる知識の分散管理: プロジェクト固有の知識を分離し、必要な時だけロード
  3. 段階的なタスク実行: 大きなタスクを小さく分割し、各ステップで独立したコンテキストを使う

次のセクションでは、Claude Codeのフック、Agent、Skillsを使って、これらをどのように実現したかを詳しく見ていきます。

Claude Code統合による品質担保と自動化

「PRレビューで毎回同じ指摘を受けるのをなんとかしたい」「どのテストを実行すればいいのか分からない」──こうした日々の小さなストレスが積み重なっていました。

Claude Codeのフック、Agent、Skillsを導入したことで、これらの悩みが解消されました。繰り返しの確認作業がフックに移り、本来集中すべき実装に時間を使えるようになりました。

3段階のフックシステム

Claude Codeのフックは、特定のタイミングでスクリプトを差し込む仕組みです。私たちは3つのタイミングでフックを設定しています。

1. SessionStart - セッション開始時の状況把握

いつ実行されるか: Claude Codeを起動した直後

「今どのブランチで作業していたっけ?」「何を変更していたっけ?」──作業を再開する際、毎回こうした確認をしていました。SessionStartフックは、起動時にプロジェクトの状況をダッシュボード形式で表示します。

表示される情報:

  • 現在のプロジェクト名
  • 作業中のブランチ名
  • 変更されたファイルの数
  • 推奨される次のアクション(/setupコマンドの実行など)

朝一番や休憩後に作業を再開する際、「このブランチでこの機能を実装していた」とすぐに思い出せます。毎回 git status を実行する手間がなくなった点は地味ですが確実な改善です。

2. PreToolUse - ファイル操作前の危険検知

いつ実行されるか: Claude Codeがファイルを書き込む/編集する直前

「うっかり本番環境の設定ファイルを変更してしまった...」そんな経験はありませんか?PreToolUseフックは、危険な操作を事前に警告してくれます。

検知するファイル:

  • node_modules/ - パッケージの直接編集
  • .env.production - 本番環境設定
  • package-lock.json - 手動での変更はNG
  • .git/ - Gitの内部ファイル

以前、Claude Codeに「パッケージのバグを修正して」と依頼したら、node_modules/を直接編集しようとしたことがありました。フックが警告を出したおかげで、「これはパッケージにPRを送るべきだ」と気づけました。

3. PostToolUse - コード品質の自動チェック

いつ実行されるか: Claude Codeがファイルを書き込んだ/編集した直後(10秒以内に実行)

これが最も効果を実感しているフックです。ファイルを編集した直後にコードパターンをチェックし、問題があれば即座に通知します。

チェック内容の例:

  • 相対パスimportの検出 → パスエイリアスの使用を提案
  • デバッグ用コード(console.log)の検出 → 削除を推奨
  • テストの.only/.skipの検出 → コミット前の削除を促す
  • 型安全性の回避(as any、@ts-ignore)の検出 → 適切な型定義を推奨
  • 英語コメントの検出 → 日本語コメントを推奨(プロジェクト規約に応じて)

PRを作成してから「console.logが残ってる」と気づくことがほぼゼロになりました。編集直後に指摘されるので、その場で修正でき、レビュアーの負担も減ります。複数のファイルを一気に編集した際の見落としを拾ってくれる点が特に助かっています。

自動チェック項目と、その重要性:

1. 相対パスの深いネスト(../../../

ファイルを移動するたびにimport文を修正するのは、地味に時間を取られる作業です。フックが相対パスを検出すると、パスエイリアス(F/, B/)の使用を提案します。リファクタリング時のimport修正がなくなりました。

2. デバッグ用コード(console.log, console.debug

以前は開発中に追加したconsole.logをそのままコミットしてしまうことが何度かありました。本番環境で不要なログが出力されるだけでなく、パフォーマンスにも影響します。フックが検出するおかげで、「消し忘れてた」という凡ミスがほぼゼロになりました。

3. テストの .only / .skip

「手元では全テストが通ったのに、CIで失敗する...」という状況が実際に起きたことがあり、原因を調べると.onlyの消し忘れだったことがありました。フックが検出してくれるので、こうした単純ミスでCIを無駄に回すことがなくなります。

4. 型安全性の回避(as any, @ts-ignore

「とりあえず動かすためにas anyで...」は、後々のバグの温床になります。フックが検出することで、「本当にこれは必要か?」と立ち止まって考える機会が生まれ、結果として型安全性の高いコードが書けるようになりました。

5. 英語コメント

プロジェクトのコーディング規約で「コメントは日本語」と決めていても、つい英語で書いてしまうことがあります。些細なことですが、チーム全体での一貫性を保つために重要です。

これらのチェックにより、レビュー前に品質問題を自動検出でき、レビュアーは本質的なロジックやアーキテクチャの議論に集中できるようになりました。

専門Agentの活用

AgentはClaude Codeに登録するタスク実行単位です。フックが「編集のたびに動く受動的なチェック」であるのに対し、Agentは「コマンドで呼び出す能動的なタスク実行」です。繰り返し発生する複雑な作業をAgent定義として保存しておき、必要な時に呼び出します。

code-reviewer Agent の詳細

このAgentが最もROIが高い取り組みでした。導入前は「PRを作成 → レビューを待つ(1〜2日) → 複数の指摘を受けて修正 → 再レビュー依頼」というサイクルを繰り返していました。私たちのプロジェクトでは初回レビューで平均8〜10件の指摘があり、その半分以上がコーディング規約や型安全性に関するものでした。

/review を実行すると、Agentは変更ファイルを読み込み、以下を確認します。

  • セキュリティ脆弱性(SQLインジェクション、XSS、認証漏れ)
  • コーディング規約違反(パスエイリアス未使用、命名規則)
  • 型安全性の問題(as any、型推論の不足)
  • パフォーマンス上の懸念(不要なre-render、メモリリーク)

このAgentが効果を発揮した理由は「プロジェクト固有のルール」を参照している点です。汎用的なlinterと違い、「このプロジェクトではパスエイリアスF/を使う」「tRPCのプロシージャ名は動詞で始める」といった規約を理解した上でレビューします。導入後、このプロジェクトでは初回レビューでの指摘件数は8〜10件から2〜3件に減り、レビュアーの時間をアーキテクチャや設計の議論に向けられるようになりました。

残り3つのAgentの概要

Agent 呼び出しコマンド 主な用途 効果の例
test-fixer /test-fix テスト失敗の原因分析と修正案提示。エラーメッセージを解析し、モックの不足やアサーションの誤りを特定する 私たちのプロジェクトではAPI仕様変更後、50ファイルのテスト修正が手作業から半自動に
component-builder /component Button atoms Atomic Design準拠のコンポーネント一式を生成。本体・テスト・Storybook・型定義を同時出力 私たちの環境では新コンポーネント追加の作業時間が30分から5分程度に
migration-creator /migration add_user_profile_table Knex.jsのマイグレーションファイルを生成。up/downと外部キー制約・インデックスをセットで出力 外部キー制約の構文ミスがほぼゼロに

Agent設計で実際に迷ったこと

複数のAgentを設計・運用する中で、いくつか判断に迷う場面がありました。

「1つのAgentに何を持たせるか」の線引き

最初に作ったAgentは「PR前に全部チェックする」万能型でした。セキュリティ・型安全性・テスト・コーディング規約をすべて1つのAgentに詰め込んだ結果、毎回3つのプロジェクト全体のドメイン知識を読み込む必要が生じ、実行に時間がかかる上に出力の精度も落ちました。

Agentを分割する判断基準として使っているのは「必要なSkillsが重複しているか」です。コードレビューにはセキュリティパターンとプロジェクト規約が必要ですが、コンポーネント生成にはAtomicDesignパターンとUI規約が必要です。必要なSkillsが異なれば、別Agentにする理由になります。

code-reviewer Agent:
- project-a Skill(ドメイン知識、コーディング規約)
- security-patterns Skill(セキュリティチェックパターン)
 
component-builder Agent:
- project-a Skill(命名規則、ディレクトリ構造)
- atomic-design Skill(コンポーネント設計パターン)

「どこまでAgentに任せるか」の境界

もう1つ迷ったのは、test-fixerのような修正系Agentに「自動修正まで許可するか」です。当初は提案のみにしていましたが、API仕様変更後に50ファイルのモック修正が必要になった際、手動修正との組み合わせで限界を感じました。

現在は「パターンが明確な修正(モックの型変更、import文の更新)は自動修正を許可、ロジックに関わる変更は提案のみ」という方針にしています。この境界は実際に失敗してみて決まったもので、最初から正解を設定できたわけではありません。

品質チェックの多段階化

Agent設計で後から気づいた効果は、チェックの段階を分けることでした。

1. PostToolUseフック(ファイル編集直後・数秒): パスエイリアス・console.log等の単純チェック
2. /quick-check(コミット前・5秒): 変更ファイルの型チェックとLint
3. /review(PR作成前・数分): セキュリティ・規約・パフォーマンスの包括レビュー

各段階でチェック対象と深度が異なるため、実行時間と網羅性のトレードオフが取れています。PostToolUseフックですべてを検出しようとすると実行が重くなりすぎ、かえって開発の流れを断ち切ります。

Skillsによる知識管理と開発フローの統一

AgentとSkillsは何が違うのでしょうか。Agentは「コマンドで呼び出してタスクを実行する」もので、呼び出しのたびに新しいセッションが起動します。一方、Skillsは「知識や手順をClaude Codeに事前登録しておく」仕組みで、スラッシュコマンドとしての呼び出しと、関連する文脈でのClaude自動参照の両方に対応しています。ドメイン知識の格納、スラッシュコマンドの定義、Claudeによる自動呼び出し──これらを1つの仕組みで統一的に扱えます。

以前はスラッシュコマンド(.claude/commands/)とSkills(.claude/skills/)は別の概念でしたが、現在はSkillsに統合されました。既存のコマンドファイルはそのまま動作しますが、新規作成はSkills形式が推奨されています。各Skillは/skill-nameでスラッシュコマンドとして呼び出せるほか、description(説明文)に基づいてClaudeが関連する場面でそのSkillを読み込むこともできます。

project-detector Skill - プロジェクト判定

何をするのか: 変更されたファイルのパスから、作業対象のプロジェクトを判定

Monorepoでは「今どのプロジェクトで作業しているのか」を把握することが重要です。project-detector Skillは、変更されたファイルのパスを見てプロジェクトを特定します。

判定ロジック:

  • apps/project-a/ → Project A
  • apps/project-b/ → Project B
  • packages/shared/ → 共有パッケージ

Claude Codeに「開発サーバーを起動して」と依頼すると、作業中のプロジェクトが判定され、適切なコマンド(npm -w @project-a/app run dev)が実行されます。「どのプロジェクトで作業しているか」を毎回指定する必要がなくなりました。

affected-analyzer Skill - 影響範囲の分析

何をするのか: 変更されたファイルから、影響を受けるパッケージとテストを特定

「共通パッケージを変更したら、どのプロジェクトに影響があるんだろう?」──こうした疑問を、affected-analyzer Skillが解決してくれます。

分析例:

変更: packages/shared/ui/atoms/Button.tsx
 
影響を受けるパッケージ:
- @project-a/app
- @project-b/app
- @project-c/app
 
実行すべきテスト:
npm run test --filter @project-a/* --filter @project-b/* --filter @project-c/*

私たちの環境では、共通パッケージを変更したら「念のため全プロジェクトのテストを実行」していました(15分)。affected-analyzer Skillで影響プロジェクトを絞り込み、該当分のみテストすることで6分まで短縮されました。

Skillsとmonorepoの相性 - ドメイン知識の効率的な管理

ここまで、タスク実行系のSkills(project-detector、affected-analyzer)をご紹介しましたが、実はSkillsの最大の強みは別のところにあります。それは、プロジェクト固有のドメイン知識やコード規約を格納できることです。

なぜSkillsにドメイン知識を書くのか?

Monorepoでは、プロジェクトごとに異なるドメイン知識が存在します。例えば:

  • Project A: ECサイトの在庫管理 → 「在庫」「発注」「配送」といった用語
  • Project B: 顧客データの分析 → 「セグメント」「コホート」「ファネル」といった用語
  • Project C: IoTセンサーデータの処理 → 「デバイス」「メトリクス」「アラート」といった用語

これらのドメイン知識を毎回Claude Codeに説明するのは非効率です。そこで、Skillsにプロジェクト固有の知識を格納しておくことで、コンテキストを無駄に消費せず、より精度の高いアウトプットを得られるようになります。

実装例: project-a Skill

# project-a Skill
 
## プロジェクト概要
ECサイトの在庫管理プラットフォーム。
 
## ドメイン知識
 
### 用語定義
- <strong>在庫(inventory)</strong>: 商品の在庫情報。倉庫と数量を持つ
- <strong>発注(order)</strong>: 商品の発注情報。発注先と数量を含む
- <strong>配送(shipment)</strong>: 発注の配送記録。配送先と状態を持つ
 
### データモデル
- inventoriesテーブル: 在庫の基本情報と倉庫位置
- ordersテーブル: 発注情報(inventory_id、supplier_id、quantity、order_date)
- shipmentsテーブル: 配送記録(order_id、shipment_date、status、tracking_number)
 
## コーディング規約
 
### 命名規則
- ファイル名: kebab-case(inventory-service.ts)
- コンポーネント名: PascalCase(InventoryList)
- 関数名: camelCase(getInventoryById)
- 定数: SCREAMING_SNAKE_CASE(MAX_INVENTORY_QUANTITY)
 
### パスエイリアス
- `F/`: src/frontend/
- `B/`: src/backend/
- `app/`: src/app/
- `M/`: __mocks__/
 
### tRPCルーター構成
- ルーターは機能単位で分割(inventory-router、order-router)
- 各ルーターは`src/backend/trpc/routers/`以下に配置
- プロシージャ名は動詞で始める(getInventory、createOrder、updateShipment)
 
### テストパターン
- Vitestを使用、vi.hoisted()でモックを定義
- ファイル名: `*.test.ts``*.test.tsx`
- テストは実装ファイルと同じディレクトリまたは`__tests__/`に配置

使い方と効果

Claude Codeに「在庫の一覧を取得するAPIを作って」と依頼すると、Skillに書かれた以下の情報が参照されます:

  1. ドメイン知識: 「在庫」が inventories テーブルに対応することを理解
  2. コーディング規約: ファイル名はinventory-router.ts、関数名はgetInventories
  3. 技術スタック: tRPCを使い、Zodでスキーマ定義
  4. パスエイリアス: B/trpc/routers/inventory-routerを使用

コンテキスト消費の削減

Skillsを使わない場合、毎回以下のファイルを読み込む必要がありました:

- README.md(プロジェクト概要)
- CLAUDE.md(開発ガイド)
- src/backend/db/migrations/(データモデル理解)
- 既存のルーターファイル(コーディング規約の参考)

Skillsを使う場合:

- project-a.md(ドメイン知識 + コード規約が1ファイルに集約)

必要な情報が1ファイルに集約されているため、コンテキスト消費を削減しつつ、同等以上の精度を実現できます。

Skillsの使い分け

私たちは、Skillsを以下のように使い分けています:

  1. プロジェクト固有Skill(project-a、project-b、project-c):

    • ドメイン知識
    • コーディング規約
    • データモデル
    • よく使うコマンド
  2. 機能横断Skill(project-detector、affected-analyzer):

    • プロジェクト判定ロジック
    • 依存関係分析
    • ビルド・テスト戦略
  3. 技術特化Skill(migration-helper、trpc-architect、component-generator):

    • 特定技術のベストプラクティス
    • コード生成パターン
    • テンプレート

monorepoとSkillsの相性が良い理由

  1. プロジェクトごとに異なる知識を整理: 3つのプロジェクトそれぞれのドメイン知識をSkillsに分離
  2. コンテキスト消費の抑制: 作業中のプロジェクトのSkillのみロード
  3. 知識の一元管理: ドメイン知識が散らばらず、1つのSkillファイルに集約
  4. 新メンバーのオンボーディング: Skillファイルを読むだけでプロジェクトの全体像を把握

Skillsを導入してから、変化がありました。ドメイン用語の誤解がほぼゼロになり、複数ファイルを毎回読み込む必要がなくなりました。新メンバーはSkillファイルを読むだけでプロジェクトの全体像をつかめるため、オンボーディングの初期コストが下がっています。コーディング規約の遵守率も、Skillから毎回参照される形になって安定しました。

コンテキスト管理の戦略

Skillsの真価は、必要な情報を必要なタイミングでのみ読み込む仕組みを実現できる点にあります。

従来の課題:

全ての情報を毎回読み込むアプローチでは、以下の問題がありました:

  • 3つのプロジェクト全てのドメイン知識を常に読み込むと、コンテキストが圧迫される
  • 使わない技術スタックの情報まで含まれ、無駄が多い
  • セッションが長くなると、コンテキストが枯渇しやすい

Skillsによる解決:

必要な情報を段階的に読み込むことで、コンテキストを効率的に使います。

作業開始時:
├─ project-detector Skill のみロード
│  → 変更ファイルから作業対象プロジェクトを判定

作業中のプロジェクトが判明後:
├─ project-a Skill のみロード(project-b、project-c は読み込まない)
│  → 必要なドメイン知識とコーディング規約のみ

特定の技術を使う時:
└─ migration-helper Skill、trpc-architect Skill など必要なもののみ追加
   → その技術のベストプラクティスとパターンのみ

読み込み量の比較:

シナリオ: Project Aで新しいAPIエンドポイントを追加する

従来のアプローチ(Skillsなし):
1. README.md 読み込み(全プロジェクトの説明)
2. CLAUDE.md 読み込み(全体の開発ガイド)
3. package.json 読み込み(全パッケージの依存関係)
4. 既存のルーターファイルを複数読み込み(参考にするため)
5. データベースマイグレーションファイルを読み込み
→ 大量のコンテキスト消費
 
Skillsアプローチ:
1. project-detector Skill でProject Aと判定
2. project-a Skill のみ読み込み
   - ドメイン知識、コーディング規約、データモデルが集約済み
3. trpc-architect Skill のみ追加(APIエンドポイント作成のため)
→ 必要最小限のコンテキスト消費

長時間セッションでの効果:

コンテキストを節約することで、以下のメリットがあります:

  • 長時間の作業セッションでもコンテキストが枯渇しにくい
  • 各タスクで必要な情報のみに集中できる
  • 複数のタスクを連続して実行しても、品質が安定する

動的なスキル切り替え:

作業内容に応じて、動的にSkillsを切り替えることも可能です:

1. Project Aのフロントエンド作業
   → project-a Skill + component-generator Skill
 
2. Project BのDB設計
   → project-b Skill + migration-helper Skill
 
3. 共有パッケージのリファクタリング
   → affected-analyzer Skill(影響範囲を分析)
   → 影響を受けるプロジェクトのSkillsのみロード

このように、Skillsを戦略的に使い分けることで、Monorepoの複雑さに対応しながら、コンテキスト管理の課題を解決できました。

スラッシュコマンドとしてのSkills

Skillsはドメイン知識の格納だけでなく、開発フローの簡素化にも活躍します。よく使う操作をSkillとして定義し、/skill-nameで呼び出せるようにすることで、複雑なAgent呼び出しをワンコマンドに簡素化できます。

/setup [project] - プロジェクトコンテキストの学習

新しいプロジェクトに参画した時や久しぶりに作業を再開する時に使います。README、CLAUDE.md、主要な設定ファイルを読み込み、Claude Codeにプロジェクトの全体像を把握させます。新しいメンバーには「まず/setupを実行してください」と伝えるだけで済むので、セットアップ手順の説明コストが下がりました。

/quick-check - 高速品質チェック

いつ使うのか: コミット前、PRドラフト作成前

変更されたファイルのみを対象に、高速で品質チェックを実行します。

チェック内容:

  • コードパターンチェック(PostToolUseフックと同じ)
  • 型チェック(変更ファイルのみ)
  • Lintチェック(変更ファイルのみ)

フルビルド(30秒)を待たずに、変更部分だけを5秒でチェックできます。コミット前のちょっとした確認に使っています。

/test-affected [pattern] - 影響範囲テスト

いつ使うのか: 共通パッケージを変更した時、大規模リファクタリング後

affected-analyzer Skillを使って、影響を受けるテストのみを実行します。

使い方:

/test-affected
# または、特定のパターンのみ
/test-affected Button

私たちの環境ではCI/CDで全テストを実行すると15分かかりますが、影響範囲のみなら6分です。

/review - PR前の包括的レビュー

いつ使うのか: PR作成前

code-reviewer Agentを呼び出し、変更されたファイル全体をレビューします。

チェック内容:

  • セキュリティ脆弱性
  • コーディング規約違反
  • 型安全性の問題
  • パフォーマンス上の懸念
  • テストカバレッジの確認

事前にチェックすることで、「PRを出してから指摘を受けて修正する」サイクルの回数が減りました。

/create-pr - PR自動作成

いつ使うのか: 実装が完了し、PRを作成したい時

変更差分を分析し、PRタイトルと説明文を生成してPRを作成します。

生成内容:

  • PRタイトル(変更内容を要約)
  • 変更内容の説明(箇条書き)
  • テストプランの提案
  • スクリーンショット配置の提案(UI変更の場合)

PR作成に10分(タイトル考える、説明書く、スクリーンショット貼る)かかっていたものがコマンド一発で完了します。説明が漏れなく書かれるため、レビュアーが変更意図を把握しやすくなりました。

Skillsとしてコマンドを定義することで、ドメイン知識の管理とワークフローを1つの仕組みで統一できます。

また、Skillsはプロジェクト固有のものだけでなく、プラグインとして外部から導入することもできます。たとえばオープンソースの「SuperPowers」を導入すると、brainstorming(設計整理)やTDD、並列エージェント実行といった汎用的なワークフロースキルが追加され、プロジェクト固有のSkillsと組み合わせて開発プロセスを標準化できます。

SuperPowers:Claude Codeの開発ワークフローを変えるプラグイン

SuperPowers:Claude Codeの開発ワークフローを変えるプラグイン

Claude Code用プラグイン「SuperPowers」の導入方法と主要スキルを解説。brainstorming(設計)、TDD(テスト駆動開発)、サブエージェント並列開発など、AIコーディングの品質を体系的に引き上げる仕組みを、ソースコードに基づいて詳しく紹介します。

開発効率化の実践例

理論だけでなく、実際に日々の開発で効果を実感できた工夫をいくつかご紹介します。

パスエイリアスの徹底

// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "F/*": ["./src/frontend/*"],
      "B/*": ["./src/backend/*"],
      "app/*": ["./src/app/*"],
      "M/*": ["./__mocks__/*"]
    }
  }
}

Before:

import { Button } from '../../../components/atoms/Button';
import { useAuth } from '../../../../hooks/useAuth';

After:

import { Button } from 'F/components/atoms/Button';
import { useAuth } from 'F/hooks/useAuth';

リファクタリング時のファイル移動が容易になり、import文の変更が不要になります。

細粒度ビルドスクリプト

// package.json
{
  "scripts": {
    "build:packages": "turbo build --filter '@repo/*'",
    "build:project-a": "turbo build --filter '@project-a/*'",
    "build:project-a-app": "turbo build --filter '@project-a/app'",
    "build:project-a-lambda-processor": "turbo build --filter '@project-a/lambda-processor'",
    "watch-build:packages": "turbo build --filter '@repo/*' --watch"
  }
}

効果:

  • Lambda関数単体のビルドが高速化
  • 開発中はwatch-build:packagesで自動再ビルド
  • CI/CDではbuild:project-aで関連パッケージのみビルド

テストDB分離による並列実行

# 各プロジェクト・Lambda関数ごとに独立したテストDB
TEST_DB_NAME=project_a_app_test
TEST_DB_NAME=project_a_lambda_processor_test
TEST_DB_NAME=project_b_app_test
 
# 並列テスト実行が可能
npm run test:project-a &
npm run test:project-b &
npm run test:project-c &

Before: テストが順次実行され、時間がかかる After: 私たちの環境では並列実行により、順次実行比で約3分の1の時間に短縮

自動コードチェックの効果

Claude CodeのPostToolUseフックにより、以下の問題をコミット前に自動検出:

# 実際の検出例
 相対パス import パスエイリアスに修正
 console.log 削除
 .only テスト 削除
 as any 型定義を追加

効果:

  • PRレビュー時間が短縮
  • レビューコメント数が削減
  • コードの一貫性が向上

おわりに

Monorepoと聞くと「管理が大変そう」「ビルドが遅くなりそう」といった懸念を持たれる方も多いかもしれません。私たちも最初はそうでした。しかし、TurborepoとClaude Codeを組み合わせることで、これらの課題を乗り越え、むしろ開発体験が向上する結果となりました。

特にClaude Codeのフックシステムは、導入当初は「またチェックツールが増えるのか...」と思っていたのですが、実際に使ってみると、レビュー前に気づける問題が格段に増え、チーム全体のコード品質が底上げされました。PRのやり取りが減り、本質的なディスカッションに時間を使えるようになったのは大きな成果です。

得られた効果:

  • ビルド時間の短縮(インクリメンタルビルドと並列実行の組み合わせ)
  • テスト時間の削減(並列実行と影響範囲を絞ったテスト実行)
  • PRレビュー時間の短縮(フックによる事前チェックで機械的な指摘が減少)
  • レビューコメント数の削減(このプロジェクトでは初回レビューでの指摘が平均8〜10件から2〜3件に)

もちろん、まだまだ改善の余地はあります。フックの検出パターンを調整したり、新しいAgentを追加したり、日々試行錯誤を続けています。完璧なセットアップなど存在しないと思いますが、少しずつ改善を重ねることで、チーム全体の開発体験が確実に良くなっていくのを実感しています。

この記事が、同じようにMonorepoの運用に悩んでいる方や、AI支援開発に興味を持たれている方の参考になれば幸いです。

// 参考文献

// SHARE
鶴田 篤広
// ABOUT THE AUTHOR
鶴田 篤広
ソフトウェアエンジニア · クレインテック株式会社
// CRANE TECH — TECHNICAL ADVISORY

AI活用で開発を加速しませんか?

AI支援開発の導入からワークフロー最適化まで、実践的なAI活用をお手伝いします。