
The 7 Ways to Steer Claude Code (and When to Use Each)
CLAUDE.md, rules, skills, subagents, hooks, output styles: the 7 ways to steer Claude Code and a practical rule for picking the right one each time.
The 7 Ways to Steer Claude Code (and When to Use Each)
Claude Code has seven ways to take instruction. Knowing which one fits each job is what separates a setup that helps from one that gets in the way.
Context
Every CLAUDE.md starts small. Three lines with the build command, one naming convention, the way to run the tests.
Six months later it has 400 lines. A deploy procedure, a security checklist, "always run prettier," "never use git push --force," style preferences, and a block nobody remembers adding. Every one of those lines is read at the start of every session and spends tokens, relevant or not.
The problem is not the content. It is the location. Claude Code offers seven mechanisms to steer its behavior, and each was designed for a different kind of instruction. Dumping everything into CLAUDE.md is using a screwdriver for every screw, nail, and bolt in the house.
This guide walks through the seven mechanisms, what each one costs, and a simple rule for choosing the right one.
The three questions that decide everything
Before the mechanisms, understand the variables. Each way of instructing Claude controls the same three things differently:
- When does the instruction enter context? At session start, on demand, or only when an event fires.
- What happens to it during compaction? Long sessions get compacted to fit the window. Some instructions survive, others vanish until touched again.
- What does it cost in tokens? Anything always loaded takes up room all the time, even when it is irrelevant to the task at hand.
Add a fourth dimension: authority. A text instruction is a request the model can interpret or forget. A hook is code that actually runs. The gap between "ask Claude to run the formatter" and "the formatter runs on its own" is the gap between the two.
1. CLAUDE.md: facts that are always true
The root CLAUDE.md loads at session start and stays the whole time. Subdirectory versions load on demand, when Claude touches a file in that folder. After a compaction, the root one is re-read; subdirectory ones disappear until accessed again.
Context cost: high. Every line is paid for in every session.
Best for: facts Claude needs at hand all the time. Build command, directory structure, coding conventions, team norms.
The practical advice is to keep it under 200 lines, with a designated owner. In monorepos, give each team a CLAUDE.md in its own subdirectory, so nobody loads coding conventions they will never touch.
The test for whether something belongs in CLAUDE.md: is it true all the time? If it is a procedure ("how to deploy") or an automation ("always format"), it belongs somewhere else.
2. Rules: scoped constraints
Rules live in .claude/rules/ as separate markdown files. They can be always loaded or limited to certain paths via paths: frontmatter. They are re-injected on compaction.
---
paths:
- "src/api/**"
---
Every API handler validates input with Zod before processing.
Context cost: medium. Low when the rule is path-scoped, because it only enters context when Claude works on files matching the pattern.
Best for: specific constraints and conventions, especially the ones that apply to only part of the codebase. A rule that only applies to src/api/** does not need to clutter context while you work on the frontend.
An unscoped rule is mechanically identical to putting the content in CLAUDE.md: always loaded, always costing tokens. The payoff shows up when you use path scoping.
3. Skills: procedures on demand
Skills live in .claude/skills/ as folders with a SKILL.md. At session start, only the name and description load. The full body enters when the skill is invoked, by command or by automatic matching to the task.
Context cost: low. The body (which can run hundreds of lines) only takes up room when it is actually used.
Best for: procedures. Deploy checklists, release processes, security review flows. That 30-line "how to do X" block bloating your CLAUDE.md is a skill waiting to happen.
The difference from CLAUDE.md is timing. CLAUDE.md is for what Claude needs to know all the time. A skill is for what it needs to know only when the task shows up. Moving procedures out of CLAUDE.md and into skills keeps the base context lean without losing the knowledge.
4. Subagents: isolated work
Subagents live in .claude/agents/ with YAML frontmatter (name, description, and optional fields for model and tool access). The name and description load at start; the body only enters when the subagent is called.
The defining trait of a subagent: it runs in its own separate context window. It does the work there, and only the final summary returns to the main conversation.
Context cost: low. Zero in the main conversation until called, and even then only the result comes back.
Best for: side tasks that would flood the conversation with intermediate results you will not re-read. Deep code search, parsing a giant log, auditing dependencies.
Think of skills versus subagents this way: a skill runs the procedure inside the main thread, where you see and steer each step. A subagent takes the work to another room and returns with just the conclusion. When the process generates noise you do not need to follow, the subagent protects your context.
5. Hooks: deterministic automation (and guarantees)
Hooks are registered in settings.json (or in a skill or agent's frontmatter) and fire on lifecycle events: file edits, tool calls, session start, and dozens of others. The code runs in the harness, outside the model's context.
There are five types: command (runs a shell command), http (sends a POST), mcp_tool (calls a tool on an MCP server), prompt and agent (use a model's judgment to decide). The first three are deterministic: they run the same way every time, without depending on Claude's interpretation.
Context cost: low. It is code in the harness, not loaded instruction.
Best for: automation that has to be reliable. Run the linter after every edit, block dangerous commands, send a Slack notification when something finishes.
Here is the key insight from Anthropic's original article. Sentences like "always do X" and "never do Y" in CLAUDE.md are the wrong tool for the job. The model choosing to run a formatter is different from the formatter running automatically. When something must happen, a text instruction is fragile; a hook is a guarantee.
A PreToolUse hook can inspect any tool call and deny it with exit code 2 (or a permission decision JSON). That is a real block, impossible to get with a written rule the model can accidentally work around.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": ".claude/hooks/lint.sh" }]
}
]
}
}
At the organization level there are managed settings, configured by an admin and impossible to override. That is how a team enforces a guardrail no individual developer can circumvent.
6. Output styles: changing the role
Output styles live in .claude/output-styles/ and are injected into the system prompt at session start. They are never compacted, and are cached after the first request.
Context cost: high. They occupy the system prompt all the time.
Best for: significant role changes. Turning Claude Code from a coding assistant into something else.
Watch the detail that trips people up: a custom output style replaces the default instructions for scope, commenting, security, and testing. You gain the new behavior and lose the defaults that came for free. The built-in styles (Default, Explanatory, Learning) cover most needs without that maintenance. Explanatory adds insights about implementation decisions; Learning asks you to write small pieces marked with TODO(human). Switching between them is a single /output-style.
7. Append system prompt: a single-session tweak
--append-system-prompt is a command-line flag passed at invocation time. The text is added to the original system prompt, without altering Claude's core role, and applies only to that run.
Context cost: moderate. It increases input tokens, but prompt caching reduces the cost after the first request.
Best for: a specific coding standard, an output format, or domain knowledge you want only for that session. Useful in scripts and one-off automations.
The limitation is honest: the more instructions you stack through this path, the less strictly Claude follows each one. It works well for small tweaks, not for rewriting the whole behavior.
The table that sums it up
| Mechanism | When it loads | On compaction | Context cost | Best for |
|---|---|---|---|---|
| CLAUDE.md | Start, persistent | Re-read | High | Facts that are always true |
| Rules | Start or by path | Re-injected | Medium (low if scoped) | Constraints and conventions |
| Skills | Name at start, body on demand | Re-injected up to budget | Low | Procedures |
| Subagents | Name at start, body on call | Only the summary returns | Low (zero until called) | Isolated tasks |
| Hooks | Lifecycle events | Outside context | Low | Deterministic automation |
| Output styles | Start (system prompt) | Never compacted | High | Role changes |
| Append system prompt | At invocation | Never compacted | Moderate | Single-session tweak |
Remember the authority difference. CLAUDE.md, rules, and append are instructions: the model interprets them and can fail. Hooks and managed settings are deterministic: they happen regardless of what the model decides. When the cost of getting it wrong is high, choose the deterministic side.
The mental model: "always," "never," "sometimes"
Forget the names for a second and look at the sentence you were about to write in CLAUDE.md. Its shape already points to the right mechanism:
- "Always do X" (an action) becomes a hook. If the formatter has to run, make it run, do not ask.
- "Never do Y" becomes a
PreToolUsehook or managed settings. When something cannot happen, an instruction is the wrong tool. - "To do Z, follow these steps" becomes a skill. Procedures live in skills, not in base context.
- "In this folder, rule W applies" becomes a path-scoped rule.
- "This is a fact about the project" stays in
CLAUDE.md. It is the one case where it is the right answer. - "Do this heavy lifting without burying me in logs" becomes a subagent.
CLAUDE.md is for facts Claude should hold all the time. A deployment runbook or a security review checklist belongs in .claude/skills/. That single distinction resolves most of the bloat.
Community reaction
Anthropic's original article quickly became a reference. Within the following week, dozens of derivative explainers appeared on blogs, Medium, dev.to, and newsletters, a sign that the decision framework filled a real gap: the tools had existed for months, but the criterion for when to use each was missing.
The most-cited friction point is path scoping for rules. Several open issues in the Claude Code repository report that the paths: frontmatter does not always behave as documented. There are reports that user-level rules with paths: (~/.claude/rules/) are ignored, that the rule is only injected when Claude reads a matching file (not when it writes one), and that the globs: format behaves more predictably than the documented paths: in some configurations.
The practical takeaway: scoped rules are powerful on paper, but test the real behavior in your setup before trusting them for an important constraint. For something that cannot fail, a hook is still the safest bet, for exactly the determinism argument the article makes.
In Practice
How to de-bloat a CLAUDE.md that became a monster:
1. Audit the current file
Read each block and sort it into four piles: fact, procedure, constraint, automation.
2. Move procedures into skills
Every "how to do X" with more than a few steps becomes a folder in .claude/skills/:
mkdir -p .claude/skills/deploy
Put the step-by-step in SKILL.md with a clear name and description, so Claude invokes it at the right moment.
3. Turn the "always" into hooks
"Always run the linter after editing" becomes a PostToolUse hook in .claude/settings.json, with an Edit|Write matcher.
4. Turn the "never" into blocks
"Never run a destructive command" becomes a PreToolUse hook that inspects the call and exits with code 2 to deny it.
5. Scope folder constraints
A convention that only applies to one area of the codebase becomes a rule in .claude/rules/ with paths:, tested in your setup.
6. Leave only facts in CLAUDE.md
What is left (commands, structure, general conventions) stays in CLAUDE.md, ideally under 200 lines.
Tip: do not try to migrate everything at once. Start with the block that bothers you most (usually the longest procedure) and turn it into a skill. The context relief is immediate and you get the hang of it.
What this changes in your day-to-day
A bloated CLAUDE.md is not just an organization problem. Every irrelevant line loaded is a token spent and model attention diluted. Spreading instructions across the seven mechanisms is not over-engineering: it is what keeps context lean and behavior reliable.
The summary fits in one sentence. Facts in CLAUDE.md, procedures in skills, scoped constraints in rules, heavy lifting in subagents, and anything that must happen in hooks. Text asks; code guarantees. Knowing the difference is what makes Claude Code work with you instead of against you.
If you want to go deeper on structuring a CLAUDE.md that pulls the best out of the agent, the Claude Code Guide covers it in detail.
References
- Steering Claude Code: skills, hooks, subagents and more: Anthropic's original article introducing the seven mechanisms and the decision framework.
- Hooks reference: official documentation for the events, the five hook types, and blocking via exit code 2 and permission decision.
- Create custom subagents: official documentation on defining subagents, the isolated context window, and what returns to the main session.
- Output styles: official documentation for the built-in styles (
Explanatory,Learning) and custom ones. - Path-scoped rules: issues in the Claude Code repository: community reports on
paths:versusglobs:behavior in rule frontmatter.