CLAUDE.mdが無視される原因は「指示バジェット」— 100命令の壁を突破する構造化設計と責務分離の実践ガイド
CLAUDE.mdに書いたルールが守られない。「コミットメッセージは日本語で」と書いたのに英語で返ってくる。「テストを必ず書いて」と指示したのにスキップされる。心当たりがある人は多いのではないだろうか。
その原因は書き方ではなく「量」かもしれない。
指示バジェットとは何か — なぜCLAUDE.mdの指示は「均一に劣化」するのか
LLMの命令従順度には上限がある
LLMが同時に従える命令の数には上限がある。命令数が増えるほど遵守率は段階的に低下していく。これを超えると、特定の指示だけが無視されるのではなく、全体の遵守率が均一に下がるという厄介な特性がある。
ここで「指示バジェット」という概念が重要になる。Claude Codeのシステムプロンプトが相当数の命令枠を消費するため、CLAUDE.mdに使える実質的な枠は限られている。
注意: 「instruction budget(指示バジェット)」はAnthropicの公式用語ではなく、コミュニティ分析から生まれた概念である。ただし、SFEIR Instituteの検証では「200行以下で92%以上の遵守率(400行超では71%に低下)」「箇条書き形式で40%向上」「命令形で94%遵守」という定量データが報告されており、実務上の指針として十分に有用だ。
CLAUDE.mdはUser Messageとして注入される
もう一つ見落とされがちな事実がある。CLAUDE.mdの内容は System PromptではなくUser Messageとして注入される。つまり、Claude Codeの内部動作に関するSystem Promptよりも優先度が低い。250行の大作CLAUDE.mdを書いても、システム側の指示と競合すれば負ける可能性がある。
正直、これを知ったときは「そういうことか」と腑に落ちた。CLAUDE.mdは万能の設定ファイルではなく、有限のリソースを使って書く設計書なのだ。
指示トリアージ — 削除・Hook化・Skill化の判断フロー
CLAUDE.mdに残すべき指示の基準
すべての指示をCLAUDE.mdに書く必要はない。以下の判断フローで振り分ける。
指示を1つ取り出す
│
├─ ESLint/Prettier/TypeScript strict等で強制できる?
│ └─ YES → CLAUDE.mdから削除(ツールに任せる)
│
├─ ファイル保存時・コミット時など特定イベントで発火する?
│ └─ YES → Hooksへ移譲
│
├─ 「/deploy」「/review」のようにコマンドで呼び出す手順?
│ └─ YES → Skillsへ移譲
│
└─ 上記いずれにも該当しない全体方針・判断基準
└─ CLAUDE.mdに残す
Hooks・Skillsへの移譲例
たとえば「コミット前にlintを通すこと」という指示は、CLAUDE.mdに書くよりHookで強制するほうが確実だ。
// .claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"command": "npx eslint --fix . && npx prettier --write ."
}
]
}
]
}
}
これでCLAUDE.mdから「lintを通してからコミットすること」という1行を削除できる。小さな1行だが、こうした積み重ねがバジェットを空ける。
注意点: Skillsにも指示バジェットへの影響がある。Skillのdescriptionはコンテキストウィンドウの1%(フォールバック8,000文字)を上限とし、各Skillは250文字で切り詰められる。大量のSkillを登録すると、それ自体がバジェットを圧迫する。
削除した指示の理由はHTMLコメントで残しておくとよい。HTMLコメントはコンテキスト注入前に自動除去されるため、トークン消費はゼロだ。
<!-- lint強制はPreToolUse Hookに移譲済み(2026-03) -->
<!-- デプロイ手順は .claude/skills/deploy.md に移譲済み -->
階層構造設計 — ルート・サブディレクトリ・@パスによる責務分散
読み込み順序と優先度
CLAUDE.mdは以下の順序で読み込まれ、後から読み込まれたものが優先される。
1. Managed policy(組織ポリシー)
2. ~/.claude/CLAUDE.md(グローバル:個人設定)
3. ./CLAUDE.md(プロジェクトルート)
4. ./src/db/CLAUDE.md 等(サブディレクトリ:オンデマンド)
重要なのは、サブディレクトリのCLAUDE.mdはそのディレクトリ内のファイルを編集するときだけ読み込まれるという点だ。これは条件付き指示を自然に分離する手段になる。
モノレポ・マルチパッケージ構成での分割戦略
my-monorepo/
├── CLAUDE.md # アーキテクチャ方針、共通コマンド
├── @shared-rules.md # @パスで共有ルールを外部化
├── packages/
│ ├── api/
│ │ └── CLAUDE.md # APIのルーティング規約、DB操作ルール
│ └── web/
│ └── CLAUDE.md # コンポーネント設計方針、CSS規約
└── .claude/
└── skills/
└── deploy.md # デプロイ手順(/deployで呼び出し)
ルートのCLAUDE.mdから共有ルールをインポートする場合は @パス 構文を使う。
# プロジェクト共通ルール
@shared-rules.md
## 技術スタック
- Runtime: Node.js 22 + TypeScript (ESM)
- DB: PostgreSQL + Drizzle ORM
@パスは相対パスで指定し、最大深度は5ホップまで。各層の役割を明確に分けることで、ルートCLAUDE.mdを軽量に保てる。
Before/After — 肥大化CLAUDE.mdのリファクタリング実例
Before: 250行の「全部入り」CLAUDE.md(抜粋)
# プロジェクトルール
きれいで読みやすいコードを書いてください。 ← 曖昧な指示
コミット前にESLintを実行してください。 ← ツールで強制可能
セミコロンは必ずつけてください。 ← Prettierで強制可能
## コーディング規約
- インデントはスペース2つ ← .editorconfigで十分
- 変数名はcamelCase ← ESLintルールで強制可能
- 関数は50行以内 ← ESLintのmax-lines-per-functionで対応可能
- anyは使わない ← tsconfig strictで対応可能
## デプロイ手順 ← 手順はSkillsに移譲すべき
1. npm run build
2. npm run test
3. git push origin main
4. vercel deploy --prod
5. 動作確認してSlackに報告
## APIレスポンス形式 ← コードスニペット直書き
すべてのAPIは以下の形式で返すこと:
{実際には50行以上のJSON例が続く...}
## 古い注意事項 ← 陳腐化
- Node 16ではESMに注意 → もうNode 22なので不要
- Webpack設定を変更しないこと → Viteに移行済み
After: 80行に圧縮した構造化CLAUDE.md
# Project: my-app
## Tech Stack
- Runtime: Node.js 22 + TypeScript (ESM, `"type": "module"`)
- Framework: Next.js 15 (App Router)
- DB: PostgreSQL + Drizzle ORM
- Test: Vitest + Testing Library
## Commands
- `npm run dev` — 開発サーバー起動
- `npm run build` — ビルド
- `npm run test` — テスト実行
- `npm run db:push` — スキーマ適用
## Architecture Rules
- MUST: Server Componentsをデフォルトとし、Client Componentは最小限にする
- MUST: DB操作は `src/db/` 内のrepositoryパターンに集約する
- MUST NOT: APIルートから直接SQLを発行しない
- MUST: エラーは `AppError` クラスでラップし、status codeを含める
- MUST NOT: `any` 型を使用しない。不明な型は `unknown` で受けてnarrowする
## Conventions
- ファイル名: kebab-case(例: `user-profile.tsx`)
- コミットメッセージ: Conventional Commits形式、日本語
- ブランチ名: `feature/`, `fix/`, `chore/` プレフィクス
## Important Context
- 認証はNextAuth v5。セッション管理の変更はチームレビュー必須
- `src/lib/legacy/` は移行中。新コードからimportしない
<!-- lint/format強制はPreToolUse Hookに移譲済み -->
<!-- デプロイ手順は .claude/skills/deploy.md に移譲済み -->
<!-- API形式の定義は src/types/api.ts を参照(スニペット直書きを廃止) -->
リファクタリング5ステップ
- 棚卸し: 現在の全指示を1行1項目でリストアップする
- 分類: 各指示を「ツール強制可」「イベント駆動」「手順的」「方針・判断基準」に振り分ける
- 移譲: ツール設定・Hooks・Skillsに移せるものを移し、CLAUDE.mdから削除する
- 圧縮: 残った指示を命令形・箇条書きに統一。「MUST/MUST NOT」で強度を明示する
- 検証: 実際にClaude Codeを動かし、指示が守られているか確認する
「MUST」「MUST NOT」のような強い表現は、「〜してください」「〜が望ましい」より明確に効く。個人的には、この書き換えだけで体感の遵守率がかなり変わった。
アンチパターン集 — CLAUDE.mdでやってはいけない5つのこと
1. コードスニペットの直書き
# NG
すべてのAPIは以下のJSON形式で返すこと:
{ "status": "ok", "data": { ... }, "error": null }
# OK
MUST: APIレスポンスは src/types/api.ts の ApiResponse<T> 型に従う
2. ツールで自動強制できるルールの記述
# NG
セミコロンを必ずつけること。インデントはスペース2つ。
# OK(CLAUDE.mdから削除し、ツール設定に任せる)
3. 曖昧・主観的な指示
# NG
きれいなコードを書いてください。適切にエラーハンドリングすること。
# OK
MUST: 関数は単一責任。1関数1エクスポートを基本とする
MUST: 外部API呼び出しはtry-catchで囲み、AppErrorに変換する
4. 陳腐化したルールの放置
すでにNode 22に移行したのに「Node 16のESM注意点」が残っていたら、それはバジェットの無駄遣いだ。
5. グローバルとプロジェクトでの重複指示
~/.claude/CLAUDE.mdに「コミットメッセージは日本語」と書いたなら、プロジェクトのCLAUDE.mdに同じことを書く必要はない。
チームでのCLAUDE.md育成プロセス
CLAUDE.mdをコードレビュー対象にする
CLAUDE.mdはgitで管理し、変更にはPRレビューを必須にする。「この指示を追加する理由は何か」「Hookで強制できないか」といった観点でレビューすることで、無秩序な肥大化を防げる。
副次的な効果として、新メンバーのオンボーディング時にCLAUDE.mdを読ませることで、プロジェクト規約の全体像を短時間で把握してもらえる。
成熟度レベルで段階的に育てる
| レベル | 状態 | 目安 |
|---|---|---|
| L0 | CLAUDE.mdなし | プロジェクト開始直後 |
| L1 | 基本ルール記述(技術スタック、コマンド、主要規約) | 〜50行 |
| L2 | 構造化+Hooks連携(命令形統一、イベント駆動の処理をHookに移譲) | 〜80行 |
| L3 | Skills分離+サブディレクトリ活用(手順のSkill化、ドメイン別CLAUDE.md) | 80行+分散ファイル |
最初からL3を目指す必要はない。プロジェクトの成長に合わせて段階的に進化させればよい。
月次の棚卸しを習慣にしよう。守られていない指示を見つけたら、原因を分析する。指示が曖昧なのか、バジェットを超過しているのか、そもそも陳腐化しているのか。原因によって対処は変わる。
CLAUDE.mdの設計は「何を書くか」より「何を書かないか」が重要だ。指示バジェットという有限リソースを意識し、ツールで強制できることはツールに、イベント駆動の処理はHooksに、手順的な操作はSkillsに委ね、CLAUDE.mdには判断基準と方針だけを残す。
まずは自分のCLAUDE.mdの行数を数えるところから始めてみてほしい。wc -l CLAUDE.md — その数字が100を大きく超えていたら、この記事が役に立つはずだ。
