はじめに
「またビルドが通らない...どのパッケージが原因だろう?」 「このコマンド、Project Aで動いたけどProject Bでは動かない...」
Monorepoを運用していると、こうした悩みに日常的に直面します。複数のプロジェクトを1つのリポジトリで管理することは、コード共有や一貫性の面でメリットがある反面、以下のような課題も抱えています。
- プロジェクト間の依存関係が複雑で、影響範囲の把握が困難
- ビルド・テスト時間が増大し、開発のテンポが落ちる
- プロジェクトごとに異なる規約があり、コード品質の担保が難しい
- 「どのコマンドを実行すればいいのか」が分かりにくい
私たちも同じ悩みを抱えていました。3つの独立したプロジェクト(それぞれ異なるDB、UIライブラリを使用)を管理する中で、「もっと効率的な方法はないか?」と試行錯誤を重ねてきました。
本記事では、TurborepoとClaude Codeを組み合わせることで、どのように開発体験を改善できたのか、実際の取り組みをご紹介します。
Monorepo構成の設計思想
3つの独立プロジェクトの共存
「全てを統一すべき」──Monorepoを設計する際、最初はそう考えていました。しかし、実際にはビジネス要件や技術的制約により、プロジェクトごとに異なる技術選択が必要になります。
- あるプロジェクトでは大量のリアルタイムデータ処理が必須のため、時系列DB対応のデータベースが不可欠
- 別のプロジェクトではドキュメント指向のデータモデルが最適で、MongoDBを採用
- UIデザインシステムも、顧客の既存ブランドガイドラインに合わせて複数のライブラリを使い分ける必要がありました
一見すると非効率に思えるかもしれません。しかし、適切な名前空間設計と共有レイヤーの最小化を意識することで、むしろ各プロジェクトが最適な技術選択をしながら、必要な部分だけを共有できる柔軟な構成を実現できました。
名前空間による明確な分離
# ワークスペース構成
workspaces/
├── @repo/* # 全プロジェクト共有パッケージ
├── @project-a/* # Project A専用パッケージ
├── @project-b/* # Project B専用パッケージ
└── @project-c/* # Project C専用パッケージ
この名前空間により、以下のメリットが得られます。
- 誤った依存関係を防止: TypeScriptがコンパイル時に検出
- プロジェクト固有の最適化: 各プロジェクトが独自の技術選択を維持
- 明示的な依存関係:
@repo/*のみが共通、それ以外は独立
共有 vs 個別化の判断基準
Monorepoで最も悩ましいのが、「何を共有し、何を個別化するか」という判断です。私たちは以下の基準で判断しました。
共有化の判断基準: 「プロジェクト間で一貫性を保つべきもの」
1. TypeScript設定(@repo/typescript-config)
チーム全体で同じ型チェックレベルを維持するため、基本的なTypeScript設定は共有化しました。strict: trueやisolatedModules: 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-processor、project-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/**", ".next/**"], // キャッシュ対象
"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のビルド完了後)
これを手動で管理する必要はありません。Turborepoが自動的に正しい順序でビルドしてくれます。
インクリメンタルビルドの効果
変更前: 全パッケージをビルド
npm run build
→ 全パッケージをビルド
変更後: 変更されたパッケージのみビルド
# Lambda関数1つだけ修正した場合
npm run build:project-a-lambda-processor
→ Lambda関数のみビルド(数秒)
# 共通UIコンポーネントを修正した場合
npm run build
→ @repo/ui + 依存する3つのアプリをビルド
効果: ビルド時間が大幅に短縮されました。
並列実行による高速化
Turborepoは、依存関係のないタスクを自動的に並列実行します。
例: Project A、B、Cのビルド(互いに依存なし)
# 順次実行の場合(導入前)
Project A → Project B → Project C
全プロジェクトを順番に実行
# 並列実行の場合(Turborepo)
Project A、B、C を同時実行
最も遅いプロジェクトの完了を待つだけ
CPUコアを最大限活用し、ビルド時間を大幅に短縮できました。
キャッシュ戦略
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での効果
導入前: 毎回全体をビルド
導入後: キャッシュを活用
- 変更されたパッケージのみビルド
- 未変更のパッケージはキャッシュから復元
- 並列実行で高速化
効果: CI/CD時間が大幅に短縮され、プルリクエストのフィードバックが高速化しました。
Turborepoのメリット:
- 開発体験の向上: ビルド待ち時間が大幅に削減
- CI/CDの高速化: 変更部分のみビルド、キャッシュ活用
- 認知負荷の軽減: 依存関係を自動解析、手動管理不要
- スケーラビリティ: プロジェクトが増えてもビルド時間が線形増加しない
Turborepoの導入により、Monorepoの「ビルドが遅い」という最大のデメリットを克服できました。
Monorepo開発におけるコンテキスト管理の課題
Turborepoでビルド時間の問題は解決しましたが、Monorepoならではの別の課題が浮上してきました。それは、AIアシスタントを使った開発におけるコンテキスト管理です。
複雑さが生む情報量の問題
3つの独立したプロジェクトを抱えるMonorepoでは、以下のような情報を扱う必要があります。
- 各プロジェクトのドメイン知識(用語、ビジネスロジック)
- プロジェクトごとに異なる技術スタック(DB、UIライブラリ)
- 共有パッケージと個別パッケージの依存関係
- コーディング規約、命名規則、ディレクトリ構造
- よく使うコマンドやビルド手順
これらすべてを一度に読み込むと、AIアシスタントのコンテキストウィンドウを圧迫してしまいます。
長時間セッションでの課題
特に大規模な機能開発や品質チェック時に、以下のような問題が発生しがちです。
- コンテキストの枯渇: 長時間の作業で過去の文脈が失われる
- 情報の混在: 複数プロジェクトの情報が混ざり、意図しない提案が生成される
- 重複読み込み: 同じルールやドメイン知識を何度も読み込む非効率さ
解決の方向性
これらの課題を解決するため、私たちは以下の戦略を採用しました。
- Agent間の責務分離: 各Agentが特定の目的のみを持ち、必要最小限の情報だけを読み込む
- Skillsによる知識の分散管理: プロジェクト固有の知識を分離し、必要な時だけロード
- 段階的なタスク実行: 大きなタスクを小さく分割し、各ステップで独立したコンテキストを使う
次のセクションでは、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/)の使用を提案してくれます。これにより、リファクタリング時の手間が大幅に削減されました。
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を定義し、繰り返し発生する複雑なタスクを自動化できます。
1. code-reviewer Agent - PR前の品質チェック
いつ使うのか: PR作成前、大きな変更を加えた後
「PRを作ったら、レビュアーから10個も指摘が来た...」こんな経験、ありませんか?code-reviewer Agentは、PR作成前に自動的にコードレビューを実行し、問題点を事前に検出してくれます。
チェック内容:
- セキュリティ脆弱性(SQLインジェクション、XSS、認証漏れなど)
- コーディング規約違反(パスエイリアス未使用、命名規則など)
- 型安全性の問題(as any、型推論の不足など)
- パフォーマンス上の懸念(不要なre-render、メモリリークなど)
使い方:
/review
たったこれだけで、Agentが変更されたファイルを自動的に読み込み、プロジェクト固有のルールに基づいてレビューを実行します。
実際の効果: 以前は「PRを作ってからレビューを待つ → 指摘を受けて修正 → 再度レビュー」という流れでしたが、事前にAgentでチェックすることで、初回のレビューで大きな指摘を受けることが減りました。レビュアーも「細かい指摘は減ったので、アーキテクチャや設計に集中できる」と好評です。
2. test-fixer Agent - テスト失敗の自動修正
いつ使うのか: テストが失敗した時、モックの更新が必要な時
「テストが失敗してるけど、なぜ失敗しているのか分からない...」そんな時、test-fixer Agentが活躍します。テストの失敗原因を分析し、修正案を提示してくれます。
何をするのか:
- テストエラーメッセージを解析
- 関連するコードとテストコードを読み込み
- 失敗原因を特定(モックの不足、アサーションの誤りなど)
- 修正案を提示、または自動修正
使い方:
npm test
# テストが失敗したら...
/test-fix
実際の効果: 特にAPI仕様変更後のテスト修正で威力を発揮しました。以前は、50個のテストファイルを手動で修正していましたが、Agentが大部分を自動修正してくれるため、作業時間が大幅に短縮されました。
3. component-builder Agent - コンポーネント生成
いつ使うのか: 新しいUIコンポーネントを作る時
Atomic Designに基づいた一貫性のあるコンポーネントを生成してくれます。
生成内容:
- コンポーネント本体(TypeScript + CSS Modules)
- テストファイル(Vitest + Testing Library)
- Storybookストーリー
- propsの型定義
使い方:
/component Button atoms
これだけで、以下のファイルが自動生成されます:
src/components/atoms/Button/index.tsxsrc/components/atoms/Button/index.module.csssrc/components/atoms/Button/index.test.tsxsrc/components/atoms/Button/index.stories.tsx
実際の効果: 以前は「新しいコンポーネントを作る → テストファイルを作る → Storybookを書く」という作業に30分かかっていましたが、Agentが5分で完成させてくれます。しかも、プロジェクトの命名規則やテストパターンに従っているため、一貫性も保たれます。
4. migration-creator Agent - DBマイグレーション生成
いつ使うのか: データベーススキーマを変更する時
Knex.jsを使ったマイグレーションファイルを自動生成してくれます。
生成内容:
- upマイグレーション(スキーマ変更)
- downマイグレーション(ロールバック)
- 外部キー制約、インデックスの適切な設定
- トランザクション処理
使い方:
/migration add_user_profile_table
実際の効果: 特に複雑な外部キー制約やインデックスの設定で助かっています。手動で書くと構文ミスが発生しやすいですが、Agentが生成したマイグレーションは常に正しい構文で、ロールバックも含めて完璧です。
Agentのメリット:
- 繰り返し発生するタスクを自動化
- プロジェクト固有のルールやパターンを学習
- 人間よりも高速で、ミスが少ない
- チーム全体で一貫性のあるコードを生成
Agentを活用することで、単純作業から解放され、より創造的な仕事に集中できるようになりました。
Agent設計のベストプラクティス
複数のAgentを運用する中で、効果的な設計パターンが見えてきました。ここでは、Monorepoでのエージェント設計において重要だと感じた原則をご紹介します。
単一責任の原則
各Agentは1つの明確な目的のみを持つべきです。これにより、以下のメリットが得られます。
メリット:
- 不要なルールやドメイン知識を読み込まなくて済む
- コンテキスト消費が最小化される
- Agentの動作が予測しやすく、デバッグも容易
例:
❌ 悪い例: universal-agent
- ビルド、テスト、レビュー、デプロイをすべて実行
- 全プロジェクトのルールを読み込む必要がある
- コンテキストが肥大化し、本来の目的が曖昧に
✅ 良い例: 目的別に分離
- code-reviewer: コードレビューのみ(セキュリティ、規約チェック)
- test-fixer: テスト修正のみ(失敗原因分析、モック更新)
- component-builder: コンポーネント生成のみ(Atomic Design準拠)
コンテキスト分離の原則
大きなタスクは、複数のAgentまたは複数のステップに分割し、それぞれ独立したコンテキストで実行します。
なぜ重要か:
- 長時間のセッションでもコンテキストが枯渇しない
- 各ステップで必要な情報のみを読み込むため効率的
- 問題が発生しても、影響範囲を特定しやすい
実践例:
大規模なリファクタリングを行う際、以下のように段階的に進めることができます。
1. 計画フェーズ
- タスクを1コミット単位に分解
- 影響範囲を特定
- 実行順序を決定
2. 実行フェーズ
- 各タスクを個別に実行
- タスクごとに独立したコンテキストを使用
- 完了後に次のタスクへ
3. 検証フェーズ
- 品質チェックAgentで検証
- テスト実行とカバレッジ確認
- 最終レビュー
このアプローチにより、各フェーズで集中すべきことが明確になり、コンテキストの無駄遣いを防げます。
必要な情報の明示化
Agentが必要とする情報を、Skillsやドキュメントとして明示的に定義します。
ポイント:
- Agentごとに必要なSkillsを明確にする
- プロジェクト固有の知識はSkillsに集約
- 毎回同じファイルを読み込むのではなく、Skillsを参照
例:
code-reviewer Agent:
- project-a Skill(ドメイン知識、コーディング規約)
- security-patterns Skill(セキュリティチェックパターン)
component-builder Agent:
- project-a Skill(命名規則、ディレクトリ構造)
- atomic-design Skill(コンポーネント設計パターン)
段階的な品質チェック
大きなタスクを実行する際は、各ステップで品質を確認しながら進めます。
メリット:
- 問題の早期発見が可能
- 修正コストが低い段階で対処できる
- 最終的な品質が安定する
実践例:
1. コード生成
2. 即座にPostToolUseフックで基本チェック
3. コミット前に/quick-checkで高速検証
4. PR作成前に/reviewで包括的レビュー
この多段階のチェックにより、品質問題を見逃すリスクが大幅に減少しました。
これらの設計原則により、Agentを効果的に活用し、コンテキスト管理の課題を解決できました。特にMonorepoのような複雑な環境では、Agentの責務を明確に分離することが成功の鍵となります。
再利用可能なSkills
AgentとSkillsの違いは何でしょうか?Agentが「タスクを自律的に実行するアシスタント」だとすると、Skillsは「再利用可能な手順書」です。複数のAgentから呼び出される共通処理を、Skillsとして切り出すことで、効率的に自動化できます。
project-detector Skill - プロジェクト自動判定
何をするのか: 変更されたファイルのパスから、作業対象のプロジェクトを自動判定
Monorepoでは、「今どのプロジェクトで作業しているのか」を把握することが重要です。project-detector Skillは、変更されたファイルのパスを見て、自動的にプロジェクトを判定します。
判定ロジック:
apps/project-a/→ Project Aapps/project-b/→ Project Bpackages/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分)するようになり、CI/CDの時間が大幅に短縮されました。
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サイトの在庫管理プラットフォーム。
## ドメイン知識
### 用語定義
- **在庫(inventory)**: 商品の在庫情報。倉庫と数量を持つ
- **発注(order)**: 商品の発注情報。発注先と数量を含む
- **配送(shipment)**: 発注の配送記録。配送先と状態を持つ
### データモデル
- 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から以下の情報を自動的に参照します:
- ドメイン知識: 「在庫」が inventories テーブルに対応することを理解
- コーディング規約: ファイル名は
inventory-router.ts、関数名はgetInventories - 技術スタック: tRPCを使い、Zodでスキーマ定義
- パスエイリアス:
B/trpc/routers/inventory-routerを使用
コンテキスト消費の削減
Skillsを使わない場合、毎回以下のファイルを読み込む必要がありました:
- README.md(プロジェクト概要)
- CLAUDE.md(開発ガイド)
- src/backend/db/migrations/(データモデル理解)
- 既存のルーターファイル(コーディング規約の参考)
Skillsを使う場合:
- project-a.md(ドメイン知識 + コード規約が1ファイルに集約)
必要な情報が1ファイルに集約されているため、コンテキスト消費を削減しつつ、同等以上の精度を実現できます。
Skillsの使い分け
私たちは、Skillsを以下のように使い分けています:
-
プロジェクト固有Skill(project-a、project-b、project-c):
- ドメイン知識
- コーディング規約
- データモデル
- よく使うコマンド
-
機能横断Skill(project-detector、affected-analyzer):
- プロジェクト判定ロジック
- 依存関係分析
- ビルド・テスト戦略
-
技術特化Skill(migration-helper、trpc-architect、component-generator):
- 特定技術のベストプラクティス
- コード生成パターン
- テンプレート
monorepoとSkillsの相性が良い理由
- プロジェクトごとに異なる知識を整理: 3つのプロジェクトそれぞれのドメイン知識をSkillsに分離
- コンテキスト消費の最適化: 作業中のプロジェクトのSkillのみロード
- 知識の一元管理: ドメイン知識が散らばらず、1つのSkillファイルに集約
- 新メンバーのオンボーディング: Skillファイルを読むだけでプロジェクトの全体像を把握
実際の効果
Skillsを導入してから、以下のような変化がありました:
- Claude Codeの回答精度が向上(ドメイン用語の誤解がほぼゼロに)
- コンテキスト消費が削減(複数ファイルを読む必要がなくなった)
- 新メンバーのオンボーディングが高速化(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の複雑さに対応しながら、コンテキスト管理の課題を解決できました。
カスタムコマンド - 開発フローの簡素化
Agentを毎回直接呼び出すのは面倒です。そこで、カスタムコマンドを定義し、よく使う操作をワンコマンドで実行できるようにしました。
/setup [project] - プロジェクトコンテキストの学習
いつ使うのか: 新しいプロジェクトに参画した時、久しぶりに作業を再開する時
Claude Codeにプロジェクトの構造、技術スタック、コーディング規約を学習させます。
何をするのか:
- プロジェクトのREADME、CLAUDE.mdを読み込み
- ディレクトリ構造を把握
- 主要な設定ファイル(tsconfig.json、package.jsonなど)を解析
- よく使うコマンドを把握
実際の効果: 新しいメンバーがチームに参加した際、「まず/setupを実行してください」と伝えるだけで、Claude Codeがプロジェクトの全体像を理解してくれます。オンボーディングが格段に楽になりました。
/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を作ったけど、レビュアーからの指摘が怖い...」という不安がなくなりました。事前にチェックすることで、自信を持ってPRを作成できます。
/create-pr - PR自動作成
いつ使うのか: 実装が完了し、PRを作成したい時
変更差分を分析し、適切なPRタイトルと説明文を自動生成してPRを作成します。
生成内容:
- PRタイトル(変更内容を要約)
- 変更内容の説明(箇条書き)
- テストプランの提案
- スクリーンショット配置の提案(UI変更の場合)
実際の効果: 以前は、PR作成に10分(タイトル考える、説明書く、スクリーンショット貼る)かかっていましたが、コマンド一発で完了します。しかも、説明が漏れなく書かれているため、レビュアーにとっても分かりやすいPRになりました。
カスタムコマンドのメリット:
- よく使う操作をワンコマンドで実行
- 複雑なAgent呼び出しをシンプルに
- チーム全体で統一されたワークフロー
- 新メンバーのオンボーディングが容易
これらのコマンドにより、開発者が覚えるべきコマンドが大幅に削減され、認知負荷が下がりました。
開発効率化の実践例
理論だけでなく、実際に日々の開発で効果を実感できた工夫をいくつかご紹介します。
パスエイリアスの徹底
// 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: 並列実行により、大幅に短縮
自動コードチェックの効果
Claude CodeのPostToolUseフックにより、以下の問題をコミット前に自動検出:
# 実際の検出例
✓ 相対パス import → パスエイリアスに修正
✓ console.log → 削除
✓ .only テスト → 削除
✓ as any → 型定義を追加
効果:
- PRレビュー時間が短縮
- レビューコメント数が削減
- コードの一貫性が向上
おわりに
Monorepoと聞くと「管理が大変そう」「ビルドが遅くなりそう」といった懸念を持たれる方も多いかもしれません。私たちも最初はそうでした。しかし、TurborepoとClaude Codeを組み合わせることで、これらの課題を乗り越え、むしろ開発体験が向上する結果となりました。
特にClaude Codeのフックシステムは、導入当初は「またチェックツールが増えるのか...」と思っていたのですが、実際に使ってみると、レビュー前に気づける問題が格段に増え、チーム全体のコード品質が底上げされました。PRのやり取りが減り、本質的なディスカッションに時間を使えるようになったのは大きな成果です。
得られた効果:
- ビルド時間の大幅な短縮(インクリメンタルビルドと並列実行)
- テスト時間の削減(並列実行と影響範囲の最適化)
- PRレビュー時間の短縮(自動チェックによる事前品質担保)
- レビューコメント数の削減(細かい指摘が減少)
もちろん、まだまだ改善の余地はあります。フックの検出パターンを調整したり、新しいAgentを追加したり、日々試行錯誤を続けています。完璧なセットアップなど存在しないと思いますが、少しずつ改善を重ねることで、チーム全体の開発体験が確実に良くなっていくのを実感しています。
この記事が、同じようにMonorepoの運用に悩んでいる方や、AI支援開発に興味を持たれている方の参考になれば幸いです。
