Claude Codeには、エージェントの振る舞いを制御する仕組みが5つあります。どれも「指示を書く」という点では同じに見えますが、**決定的に違うのは「いつ読み込まれるか」と「助言か強制か」**です。
同じ一文でも、置き場所を変えるだけで効きかたが変わります。この記事では5つの違いと選び方を整理します。
結論:3つの軸で分かれる
| 置き場所 | 読まれるタイミング | 効力 | |
|---|---|---|---|
| CLAUDE.md | ./CLAUDE.md |
毎セッション必ず | 助言 |
| rules | .claude/rules/*.md |
paths: の条件が合った時 |
助言 |
| skills | .claude/skills/<名前>/SKILL.md |
呼び出した時 | 助言 |
| サブエージェント | .claude/agents/<名前>.md |
依頼した時(別コンテキスト) | 助言 |
| hooks |
.claude/settings.json(JSON) |
指定イベントで必ず | 強制 |
hooksだけ形式がJSONで、他はすべてMarkdownです。これは読み手が違うためで、Markdownの4つは「Claudeに読ませるテキスト」、hooksは「Claude Code本体が実行する設定」です。
┌──────────────────────────────────────────────┐
│ Claude が読む(助言) │
│ │
│ CLAUDE.md rules skills │
│ ↓ ↓ ↓ │
│ 毎セッション 条件つき 呼んだ時 │
│ │
│ サブエージェント → 別コンテキストで動く │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Claude Code が実行する(強制) │
│ │
│ hooks → シェルスクリプトを起動、拒否できる │
└──────────────────────────────────────────────┘
前提:長く書くほど効かなくなる
Claudeは渡された指示を一度に読みます。指示が増えるほど一つひとつの重みは薄まり、あるところから「書いてあるのに従わない」状態になります。能力の問題ではなく分量の問題なので、対処は削ることしかありません。
公式ドキュメントもCLAUDE.mdについて「200行以内」を目安として挙げており、各行について「これを消したらClaudeは間違えるか?」を問い、答えが「いいえ」なら消すことを勧めています。
つまり設計の目標は「必要な指示を、必要な時にだけ届ける」ことになります。そのための置き場所が5つある、という構図です。
CLAUDE.md
毎セッション必ず読まれる、唯一の常時ロードです。frontmatterは不要で、ただのMarkdownを書きます。
## 実装方針
- 時刻はUTCで保存し、API入出力はISO 8601形式とする
- 秘密情報は.envから読み込み、ログ・DBへ出力しない
書くのは「場所を問わず、知らなければ必ず間違えること」です。
| 書く | 書かない |
|---|---|
| 推測できないコマンド(ビルド、テスト実行) | コードを読めば分かること(構成、依存一覧) |
| 言語の標準と異なる規約 | 言語の標準的な慣習 |
| プロジェクト固有の設計判断とその理由 | 頻繁に変わる情報(進行中の計画、現在地) |
| 環境の癖(必須の環境変数、起動順) | 詳細なAPI仕様(リンクで足りる) |
| 非自明な落とし穴 | 「きれいなコードを書く」のような自明な指示 |
| ブランチ名やコミットの作法 | 同じ内容の言い換え |
特に見落としやすいのが最後の行です。同じ規約を「実装方針」「テスト方針」「完了条件」の3箇所に少しずつ言い換えて書いてしまうことは多く、これは分量を3倍にして効きめを下げるだけの変更になります。
/init で雛形を生成でき、/context の Memory files で実際に読み込まれたか確認できます。
rules
CLAUDE.mdをトピック別に分割する仕組みです。最大の価値は paths: で読み込み条件を絞れることにあります。
---
paths:
- "app/db/**"
---
# マイグレーション規約
- 適用済みファイルは編集しない(再実行されないため)
こう書くと、app/db/ 配下のファイルをClaudeが読んだ時にだけこの規約が注入されます。関係ない作業をしている間はコンテキストを消費しません。
ここが重要なのですが、paths: を書かないruleはCLAUDE.mdと同じタイミング・同じ優先度で読まれます。 つまりファイルを分割しただけでは1バイトも軽くならず、paths: を書けるかどうかが唯一の判断基準になります。
~/.claude/rules/ に置けば全プロジェクトに適用され、シンボリックリンクによる複数プロジェクト間の共有もできます。
skills
呼び出した時だけ読まれる手順書です。ディレクトリを切って SKILL.md を置きます。
---
name: run-job
description: バッチジョブを手動実行し、ログを追って結果を確認する
argument-hint: [ジョブ名]
allowed-tools: Bash(docker compose exec:*), Read, Grep
---
$ARGUMENTS のジョブを実行する。
## 1. 実行する
## 2. ログを確認する
## 3. DBへの反映を確認する
rulesとの違いは**「守るもの」か「やるもの」か**です。制約・規約はrules、順番のある作業はskills。
/名前 で明示的に呼べるほか、description が合致すればClaudeが自発的に使います。副作用のある作業は disable-model-invocation: true を付けて自動起動を止められます。
サブエージェント
唯一、別のコンテキストで動く仕組みです。読んだファイルの中身は本体に残らず、結論だけが返ります。
---
name: design-qa
description: 設計書と実装の内容に関する調査・質問回答
tools: Read, Grep, Glob
model: opus
---
(ここがサブエージェントのシステムプロンプト)
本体 サブエージェント
┌──────────────┐ ┌──────────────────┐
│ │ ── 依頼文 ──────▶ │ 設計書5冊を読む │
│ 文脈は │ │ ↓ │
│ 増えない │ ◀── 結論だけ ──── │ 中身はここに残る │
└──────────────┘ └──────────────────┘
注意点として、会話の文脈は渡りません。渡るのは依頼文だけなので「さっきの件を確認して」は通じず、ファイル名も章番号も依頼文に書ききる必要があります。結果も画面には出ず、Claudeが要約して伝えます。
tools を絞れるのが安全設計の要点です。調査専用なら Read, Grep, Glob だけにしておけば、間違ってもファイルを書き換えません。
hooks
唯一の強制力です。他の4つはClaudeが判断を挟みますが、hooksはシェルスクリプトとして必ず実行されます。
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit",
"hooks": [{
"type": "command",
"if": "Edit(app/db/migrations/*.sql)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block.py"
}]
}]
}
}
Claudeが ┌──────────┐ 許可 ──▶ ツールが動く
ツールを呼ぶ ──────▶ │ hook │
└──────────┘ 拒否 ──▶ 実行されない
Claudeの判断を経由しない
イベントは30種類以上ありますが、日常的に使うのは4つです。
| イベント | いつ | 用途 |
|---|---|---|
PreToolUse |
ツール実行の直前 | 禁止操作をブロック(唯一の拒否権) |
PostToolUse |
実行成功の直後 | 編集後のlint・format |
Stop |
応答を終える時 | テストが通るまで終わらせない |
SessionStart |
起動・再開時 | 最新状態をコンテキストに注入 |
ブロックの書き方は2通りあります。stderrにメッセージを書いて exit 2 するか、JSONで permissionDecision: "deny" を返すかです。後者は "escalate" にすると「禁止」ではなく「ユーザーに確認を求める」になります。
if フィールドで対象を絞れるのも重要で、if: "Bash(rm *)" のように書けばBash全体をフックせずに済みます。
なお、スクリプト本体は別ファイル(.claude/hooks/*.py など)に置けます。settings.jsonに書くのは「いつ・何にマッチしたら・どれを起動するか」の登録部分だけです。
紛らわしい4つの境界
CLAUDE.md と rules
分割そのものに意味はありません。paths: を書けるかどうかだけが違いで、書かなければ両者は完全に同じ挙動になります。
rules と skills
守るものか、やるものか。「適用済みマイグレーションは編集しない」は守るものなのでrules。「ジョブを実行してログを確認して報告する」はやるものなのでskills。
skills とサブエージェント
手順をClaude自身が実行するならskills。大量に読んで結論だけ欲しいならサブエージェント。前者は本体のコンテキストで動き、後者は別コンテキストで動きます。
助言4つと hooks
「必ず○○する」と書いたのに守られないなら、それは最初からhooksで書くべきものでした。逆に、たまに例外があってよい規約をhooksにすると作業の妨げになります。
選び方
場所を問わず毎回必要か ────── はい ──▶ CLAUDE.md
│ いいえ
▼
特定のパスでだけ必要か ────── はい ──▶ rules(paths: つき)
│ いいえ
▼
複数ステップの手順か ──────── はい ──▶ skills
│ いいえ
▼
大量に読む/別の視点が要る ── はい ──▶ サブエージェント
───────────────────────────────────────────────
※ 例外を許さない処理だけは、この流れの外で hooks に書く
つまずきやすいところ
-
ファイルを分けても軽くならない。
paths:を指定して初めて効果が出ます。@importも同様で、起動時に全部展開されるため軽くなりません - skillが呼ばれないのは、CLAUDE.mdと重複しているから。呼ばなくても同じ結果になるなら、呼ぶ動機は生まれません
- skillに書いた数値は古くなる。「テストは全340件」のような記述は、放置すると判断基準として機能しなくなります
- 強制は最小限に。判定が機械的で誤検知しないものだけをhooksにします。曖昧な判定を強制にすると、正しい作業まで止まります
-
/compact後に自動で戻るのはプロジェクト直下のCLAUDE.mdだけ。paths:付きruleは、次に該当ファイルを読むまで戻りません - 設定は書いた時点では動作していない。ruleは対象ファイルを開いて、hooksは禁止したい操作を実際に試して、期待どおり働くか確かめる必要があります
指示どおりに動かない時、原因はたいてい3つのどれかです。分量が多すぎて埋もれている、表現が曖昧で解釈が割れている、そもそも助言では足りず強制が必要だった。書き足すのは最後の手段で、まず削るか、具体的に書き直すか、hooksにするかを検討するのが近道です。
整理の進めかた
すでに肥大化したCLAUDE.mdがある場合、次の順で進めると手戻りが少なくなります。
- 消せる行を先に探す — コードから読めること、役目を終えた計画、重複した言い換え。これだけで大きく減ることが多い
- 残った行に条件が付くか見る — 特定のディレクトリでしか意味を持たない規約はrulesへ移す
- 手順になっているものを切り出す — 番号付きのステップが並んでいたら、常時読ませる必要はない
- 「必ず」と書いてある行を疑う — 本当に例外を許さないなら、助言のままでは不足している
- 最後に実際に試す — 設定は書いた時点では動作していない
なお /doctor を実行すると、CLAUDE.mdの削減候補を提案してくれます。ディレクトリ構成や依存一覧のような「コードから導ける情報」を削り、落とし穴や理由、標準と異なる規約を残す方向で提案されます。