Skip to content

Steering Claude Code: choosing between CLAUDE.md, rules, skills, subagents, and hooks

Anthropic published a decision framework (June 2026) for Claude Code’s seven customization layers: CLAUDE.md, rules, skills, subagents, hooks, output styles, and --append-system-prompt. The core axis is context cost vs. instruction authority — and the single most important distinction is that everything except hooks is advisory. Instructions in context can be ignored under pressure or prompt injection; hooks are the only mechanism enforced by the harness itself. The community distilled it to: “Hooks are law, CLAUDE.md and skills are guidance, subagents are scoped workers.”

Layer Load timing Context cost Best for
CLAUDE.md (root) Session start, re-read on compaction High Project fundamentals
CLAUDE.md (subdir) On-demand Low Subdirectory conventions
Rules Session start or path-scoped Medium Constraints and standards
Skills Name/description at start; body on invocation Low Procedural workflows
Subagents Isolated context; only final message returns Low Parallel side tasks
Hooks Lifecycle events, bypasses compaction Low Deterministic automation
Output styles / append Session start (system prompt) High Significant role changes

Project fundamentals the model should always know: build commands, layout, conventions. Keep it under 200 lines; it’s delivered as a user message, not the system prompt, so compliance isn’t guaranteed. Root CLAUDE.md re-injects after /compact; nested ones don’t auto-reload.

Example — facts, not procedures:

# Acme API
- Monorepo: `apps/api` (Fastify), `apps/web` (Next.js), `packages/shared`.
- Run tests with `pnpm test --filter <app>`; never run the full suite locally.
- All timestamps are stored as UTC ISO-8601 strings.

Markdown files in .claude/rules/; with a paths: frontmatter they load only when Claude touches matching files, keeping instructions out of context during unrelated work. Unscoped rules load always, at the same priority as CLAUDE.md.

Example — .claude/rules/api-handlers.md:

---
paths:
- "src/api/**"
- "**/*.handler.ts"
---
All API handlers must validate input with Zod. Migrations are append-only —
never edit an existing migration file.

Folders under .claude/skills/<name>/SKILL.md. Only name + description load at session start; the body loads on invocation (via /name or auto-matching). Create one when “you keep pasting the same checklist into chat, or a section of CLAUDE.md has grown into a procedure rather than a fact.” As of June 2026, skills and custom slash commands (.claude/commands/) are unified — both create the same /command.

Example — .claude/skills/release/SKILL.md:

---
name: release
description: Cut a release — changelog, version bump, tag, GitHub release
---
1. Run `pnpm test` and `pnpm build`; abort on failure.
2. Update CHANGELOG.md from commits since the last tag.
3. Bump the version (`$ARGUMENTS` = patch|minor|major), commit, tag `vX.Y.Z`.
4. Push with tags and create a GitHub release with the changelog section.

Markdown files in .claude/agents/ with YAML frontmatter (name, description, tools, model). A subagent runs in a fresh context window; only its final message returns. Use when a side task would flood the main thread with output you won’t reference again. Rule of thumb vs. skills: skill when you want to watch and steer each step; subagent when you only want the conclusion. Community estimates put overhead at ~20k tokens per spawn (as of 2026-06, unverified), so reserve them for genuine isolation needs.

Example — .claude/agents/log-analyst.md:

---
name: log-analyst
description: Digs through server logs to find the root cause of an error
tools: Read, Grep, Bash
model: haiku
---
You analyze log files under ./logs. Given an error description, find the
first occurrence, correlate request IDs across services, and report only:
root cause, affected time window, and the three most relevant log lines.

Commands that fire on lifecycle events (PreToolUse, PostToolUse, SessionStart, Stop, …), configured in settings.json. Exit code 2 blocks the action and feeds stderr back to Claude — this is the only layer the model cannot ignore. Anti-pattern: writing “never do X” or “always do Y after Z” as prose in CLAUDE.md; that belongs here.

Example — block edits to production config (.claude/settings.json):

{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-prod-config.sh"
}
]
}
]
}
}

where the script exits 2 with a message on stderr if the target path matches config/production/**.

Files in .claude/output-styles/ injected into the system prompt. Caveat: an output style replaces Claude Code’s default coding instructions unless keep-coding-instructions: true is set. Built-ins (Explanatory, Learning) cover most needs.

Example — .claude/output-styles/mentor.md:

---
name: Mentor
description: Explains every change like a senior engineer teaching a junior
keep-coding-instructions: true
---
After each code change, add a short "Why this way" note covering the
trade-off you made and one alternative you rejected.

7. --append-system-prompt — per-invocation additions

Section titled “7. --append-system-prompt — per-invocation additions”

A CLI flag that adds to the system prompt without replacing anything. Must be passed on every invocation, so it’s mainly for scripts and CI rather than interactive use.

Example — a CI review job:

Terminal window
claude -p "Review the diff in this PR for security issues" \
--append-system-prompt "You are a security reviewer. Report only \
exploitable findings with severity; do not comment on style."
  1. “Always do X after Y” in CLAUDE.md → should be a PostToolUse hook.
  2. Hard guardrails (“never do this”) as prompts → hooks with exit-code blocking.
  3. Multi-step procedures living in CLAUDE.md → move to skills.
  4. Unscoped rules that load during unrelated work → add paths:.
  5. Defaulting to subagents when a single-threaded skill would do.
  6. Personal preferences in project files → put them in ~/.claude/ or CLAUDE.local.md.
  • /ai-news lives as a skill (.claude/skills/ai-news/SKILL.md); its research phase delegates to subagents (throwaway intermediate output).
  • Note-writing conventions are a path-scoped rule (.claude/rules/notes.md) that loads only when editing src/content/docs/**.
  • A PostToolUse hook (.claude/hooks/validate-note.sh) validates note frontmatter on every edit — enforcement, not advice.