巨大化したモノリスでAIエージェントが余計なコードを触らないようにするディレクトリ整理術
数十万行の単一リポジトリに Claude Code や Aider を導入すると、関係のないファイルから読み込み始めます。モデルのコンテキストウィンドウには限りがあるにもかかわらず、無関係なファイルまでかき集めてトークンの上限に達し、見当違いのコードを修正してしまいます。
この問題は、プロンプトを長く書くだけでは解決しません。エージェントが探索するコードの物理的な範囲を狭め、機械的に検証するルールをローカルに組み込む必要があります。
1. 循環参照を引き起こすディレクトリの分離
モノリスリポジトリでエージェントが無駄な探索に陥るポイントは、大体3つあります。雑多なヘルパー関数が集まった共通ユーティリティフォルダ(src/utils/)、ビジネスロジックが絡み合うサービスレイヤー(src/services/)、グローバルモデルディレクトリ(src/models/)です。これら3つのフォルダが互いに参照し始めると、エージェントはコードを1行修正するために数十ものファイルを読み込みます。
技術階層ごとに分かれていたフォルダをドメイン単位でまとめて隔離すると、探索範囲が狭まります。
| 区分 |
レイヤー中心構造 |
ドメイン分離構造 |
エージェントの動作の変化 |
| フォルダ基準 |
技術階層の分離 (/controllers, /services) |
ドメインの分離 (/domains/order) |
必要なファイルのみを1つのフォルダ内で探索 |
| 依存関係の接続 |
グローバルエンティティを直接インポート |
ドメインインターフェースの境界で通信 |
無関係なファイルまで連鎖的に読み込む現象をブロック |
| 共通ロジック |
単一の src/utils/ に関数が混在 |
ドメイン専用ユーティリティと共通パッケージに分離 |
不必要なグローバルコンテキストの汚染を防止 |
作業中のサービスを止めずにディレクトリを移行する手順は以下の通りです。
- エージェントが過剰に呼び出している依存関係を確認し、分離するドメインを決定します。
- ドメイン間の直接参照を断つため、サービス境界インターフェースを作成します。
- 関連するビジネスロジックを
src/domains/{ドメイン名}/ フォルダに移動し、tsconfig.json のパスエイリアスを更新します。
- 分離したサブディレクトリの中に、そのドメイン専用の設定ファイル(
CLAUDE.md)を配置します。
2. 曖昧な自然言語のルールを数値制約に変更する
自然言語で長く書かれたコーディング規約は、エージェントに見過ごされがちです。設定ファイルの上部に明確な数値と禁止事項を配置することで、指示を正確に従わせることができます。
`markdown
プロジェクトの制約事項 (CLAUDE.md の上部に配置)
- セキュリティおよび例外処理
- NEVER allow raw SQL string concatenation. ALWAYS use parameterized queries with ORM.
- NEVER throw generic Exception or Error. ALWAYS throw domain-specific exceptions inheriting from BaseDomainException.
- ALWAYS enforce tenant_id filtering in all database queries under src/domains/.
- コード構造の数値制約
- Functions MUST NOT exceed 40 lines of code.
- Cyclomatic complexity MUST be kept under 8 per function.
- ALWAYS return Result<T, E> pattern for business layer operations instead of null.
`
設定ファイルが200行を超えると、後方の指示事項が抜け落ちる頻度が高くなります。
- ルートディレクトリの
CLAUDE.md には、ビルドコマンドとグローバルコミットルールのみを200行以内に抑えて維持します。
src/domains/order/ などの下層フォルダのルールは、該当ディレクトリ内の専用ルールファイルに分散させます。
- 開発者個人の設定は
CLAUDE.local.md に記述し、.gitignore に登録してコンフリクトを防ぎます。
3. ローカルフックによるエージェントの修正コード自動検証
エージェントが作成したコードの構文エラーやリグレッションバグは、コミットのタイミングで自動的に検知する必要があります。Goの単一バイナリで動作する Lefthook を使用すると、Node.jsベースのツールよりも軽量に並列チェックを実行できます。
ルートに lefthook.yml を配置し、軽量な静的チェックと重いテストのステップを分離します。
`yaml
pre-commit:
parallel: true
commands:
linter:
glob: ".{ts,tsx}"
run: npx eslint --fix {staged_files}
stage_fixed: true
formatter:
glob: ".{ts,tsx,json,md}"
run: npx prettier --write {staged_files}
stage_fixed: true
security-scan:
run: gitleaks git --staged --no-banner
pre-push:
parallel: false
commands:
typecheck:
run: npx tsc --noEmit
unit-tests:
run: npm run test:unit -- --passWithNoTests
`
コミット段階の pre-commit ではステージングされたファイルのみを10秒以内にチェックし、全体の型チェックと単体テストはプッシュ段階の pre-push に委ねます。
エージェントが --no-verify オプションを使用してフックを回避できないように、.claude/hooks/block-no-verify.mjs インターセプターを登録します。
`javascript
import fs from 'fs';
const input = fs.readFileSync(0, 'utf8');
const parsed = JSON.parse(input);
if (parsed.tool_input?.command?.includes('--no-verify')) {
console.error("Policy Violation: --no-verify flag is strictly prohibited.");
process.exit(1);
}
process.exit(0);
`
フックが失敗すると、コンソールのエラー出力が次のプロンプトに入力され、エージェントが自らコードを修正します。
4. 探索スコープの制限によるトークンの無駄遣い防止
エージェントがビルド成果物やロックファイルを読み込み始めると、トークンが急速に消費されます。ファイル探索の範囲をあらかじめ狭めておくことで、意図しないコストの発生を防ぐことができます。
プロジェクトのルートに .ignore または .aiderignore を作成し、大容量の成果物を登録します。
`text
node_modules/
dist/
build/
coverage/
*.min.js
*.svg
*.lock
package-lock.json
public/assets/
db/migrations/
`
ターミナルで実行する際にも作業対象のディレクトリを明示し、グローバルスキャンを遮断します。
`bash
aider "Refactor Order validation logic" --path=src/domains/order/ --exclude=src/domains/order/tests/
`
コードを修正する前に計画モードで変更内容を先堂に確認し、失敗する単体テストを作成してからパスするコードを書かせるようにすると、エージェントの作業範囲が安全に保たれます。