There’s a conversation that seems inevitable once a team adopts an agentic coding tool. “I put it in CLAUDE.md — why did it still do that?” The usual answer is that the file has grown too long and nobody reads it anymore. That may be true, but it misses the real problem.
The real problem is that the tool gives you several different ways to steer it, and they are not interchangeable. A memory file, a skill, a hook, and a subagent differ in when they load, how much context they cost, whether they survive compaction, and — most importantly — whether they’re a suggestion or a guarantee. Put an instruction in the wrong one and it either gets ignored, burns context on every turn, or quietly disappears halfway through a long session.
Anthropic recently published a short, dense article on exactly this: Steering Claude Code: skills, hooks, rules, subagents and more. This post is my engineer’s synthesis of it and of the related articles on CLAUDE.md, hooks, skills, and subagents — with my own opinions on where each mechanism earns its place. It’s a companion to the AI-native SDLC post, which zooms out to the whole delivery loop; here we zoom into the harness itself.
The one question that sorts everything
The question I’d ask about any piece of guidance:
Is this knowledge, a procedure, a guarantee, or a separate job?
- Knowledge — facts the agent needs most of the time: build commands, directory layout, naming conventions. That’s a memory file.
- Procedure — a multi-step “how we do X” that matters sometimes: release checklist, migration recipe, security review. That’s a skill.
- Guarantee — something that must happen (or must never happen) regardless of what the model decides: formatting, blocking writes to protected paths, refusing to finish while tests fail. That’s a hook (plus permissions).
- Separate job — work whose intermediate mess you don’t want in your main conversation: reading forty files to answer one question, an unbiased review, log analysis. That’s a subagent.
Most steering mistakes are category errors between these four. Let’s take them one at a time.
CLAUDE.md: the always-on map
The project memory file is loaded at the start of every session and stays there. That’s its strength and its cost. Everything in it competes for attention on every single turn, including the turns where it’s irrelevant.
What belongs there is the stuff a competent new hire would need on day one and would otherwise ask about repeatedly: how to build and test, where things live, the conventions that aren’t obvious from the code, and the project-specific traps. The CLAUDE.md article frames it well — every line should exist because it fixed a real problem you actually hit, not a hypothetical one. The steering article gives a concrete ceiling: keep the root file under 200 lines, give it an owner, and review changes to it like code.
A good root file reads like a terse onboarding note:
# CLAUDE.md
## Commands
- `npm run dev` — local server on :4321
- `npm run check` — lint + types + i18n parity. Must pass before commit.
## Layout
- App lives in `src/`, not the repo root. Content in `src/src/content/`.
## Conventions
- Every new UI string goes into all four locale files.
- Never construct canonical URLs from the request URL.
Easy to miss:
- Subdirectory files are lazy. A CLAUDE.md inside a subdirectory loads only when the agent reads a file in that directory. That makes it cheap — and it also means it can drop out after compaction until that directory is touched again. In a monorepo, this is how each team gets its own conventions without taxing everyone else’s sessions.
- Personal preferences don’t belong in the project file. Your preferred commit tone or editor quirks go in your user-level file. The project file is for team conventions specific to this codebase.
- No secrets, ever. It becomes part of the system prompt; treat it as potentially public.
Rules: CLAUDE.md with a scope
Rule files in .claude/rules/ are the bridge between “always loaded” and “loaded when relevant.” Without scoping, a rule behaves like more CLAUDE.md. Add a paths field and it only loads when the agent touches matching files:
---
paths:
- "src/api/**"
---
All API handlers validate input with Zod before any processing.
This is the right home for cross-cutting constraints that apply to some of the codebase — API handlers, migrations, generated clients — and would be noise everywhere else. A path-specific rule without a path scope is one of the cheapest anti-patterns to fix.
Skills: procedures on demand
A skill is a folder with a SKILL.md at its root — a name, a description, and a body of instructions — plus optionally scripts, templates, and reference files. The key mechanism is progressive disclosure: only the name and description sit in context up front; the body loads when the skill is invoked (by slash command or because the agent matched the description to the task), and bundled files load only if the instructions point to them. The skills explainer puts the metadata cost at roughly 100 tokens per skill and the full instructions under 5k tokens, which is why you can have many available without crowding the window.
---
name: release-checklist
description: Use when preparing a release or tagging a version. Bumps
the version, updates CHANGELOG, runs the full check suite, drafts notes.
---
1. Confirm the working tree is clean and on main.
2. Run `npm run check`; stop and report on any failure.
3. ...
Two things matter more than anything else in that file:
- The description is the trigger. It’s the only part the agent sees before deciding whether to load the skill. Write it as “what it does and when to use it,” using the words people actually type. A vague description means the skill never fires, or fires on everything.
- The body is a procedure, not an essay. Numbered steps, explicit stop conditions, pointers to scripts for anything deterministic. If a step is “run this exact command,” put the command in a script the skill calls rather than hoping the model reconstructs it.
The steering article’s heuristic is the one I use most: if you find a 30-line procedure living in CLAUDE.md, it’s a skill that hasn’t been extracted yet. It’s paying full context rent on every turn to be useful on a few.
Skills also compose with connectors: MCP gives the agent access to your issue tracker; a skill tells it how your team triages.
Hooks: the only thing that’s actually guaranteed
Here’s the line I’d underline in the whole topic. Everything above — CLAUDE.md, rules, skills — is advisory. The model reads it and, usually, follows it. “Usually” is fine for conventions. It’s not fine for “never write to migrations/ without approval” or “always run the formatter.”
Hooks are different in kind. They’re handlers registered in settings that fire on lifecycle events — before a tool runs, after it runs, when a session starts, when the agent tries to stop, before compaction, and so on. Command, HTTP, and MCP-tool hooks run deterministically: no model judgment involved. A PreToolUse hook that exits with code 2 blocks the tool call, and its stderr is fed back so the agent understands why.
A minimal “format after every edit” hook in .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format.sh" }
]
}
]
}
}
The script reads the tool input as JSON from stdin, pulls out the file path, and runs your formatter. Formatting is no longer something the agent might remember; it just happens.
The steering article states the rule plainly: an instruction isn’t a guardrail. If your CLAUDE.md says “always do Y after X,” that’s a hook. If it says “never do Z,” that’s a hook or a permission rule — and for organization-wide policy, managed settings that individual users can’t override locally.
A few cautions from the hooks guide that I’d treat as non-negotiable:
- Hooks run with your user permissions. They’re arbitrary shell. Review them like production code, quote your variables, use absolute script paths, and sanitize what comes in on stdin.
- Matchers are case-sensitive.
bashwon’t match theBashtool. This is the kind of bug that makes a guardrail silently not exist. - Not every hook is deterministic. Prompt- and agent-type hooks use model judgment. Useful, but don’t mistake them for enforcement.
- Start with one. The guide suggests the formatter: low risk, immediate payoff.
Subagents: a separate context for a separate job
A subagent is an isolated agent instance with its own context window, its own system prompt, and its own tool permissions. It starts without your conversation’s history, does a job, and returns only its final message. That isolation is the whole point.
Custom subagents live in .claude/agents/ (project) or ~/.claude/agents/ (personal) as Markdown with frontmatter:
---
name: security-reviewer
description: Reviews a diff for security issues before commit. Use after
implementation, before opening a PR.
tools: Read, Grep, Glob
---
You are a security reviewer. You do not edit files. Report findings
by severity with file and line, and say explicitly if you found none.
Notice what’s doing the work: a read-only tool list (it can’t “helpfully” fix things) and a description that says when to delegate, because that description is what drives automatic delegation.
The subagents article offers a rule of thumb I find genuinely useful: if a task means exploring ten or more files, or you have three or more independent pieces of work, that’s a strong signal to delegate. The other strong cases are a fresh perspective (a reviewer that never saw your debugging detours) and verification before committing.
Equally useful is when not to: sequential work where each step needs the previous step’s full output, edits to the same file in parallel, small tasks where the delegation overhead outweighs the gain, and agents that need to talk to each other. Each subagent burns its own tokens — the token economics of a fan-out are real.
The steering article’s distinction between a skill and a subagent is the clearest I’ve seen: use a subagent when the intermediate results would clutter your conversation; use a skill when you want to see and steer each step.
The decision table
Here’s the whole thing on one screen. The loading and compaction columns come from the steering article; the “use it for” column is my condensation.
| Mechanism | Loads | After compaction | Advisory or enforced | Use it for |
|---|---|---|---|---|
| Root CLAUDE.md | Session start | Re-read | Advisory | Commands, layout, day-one conventions |
| Subdirectory CLAUDE.md | When that directory is read | Lost until touched again | Advisory | Per-package or per-team conventions |
| Path-scoped rule | When matching files are touched | Re-injected | Advisory | Constraints for part of the codebase |
| Skill | Description at start, body on invoke | Re-injected within a shared budget | Advisory | Procedures used sometimes |
| Subagent | Description at start, body when called | Only the summary returns | Advisory (tools are enforced) | Isolated, noisy, or parallel side jobs |
| Hook | On lifecycle events | Bypasses compaction | Enforced (command/HTTP/MCP types) | Must-happen and must-never-happen |
| Permissions / managed settings | Always | n/a | Enforced | Hard boundaries, org-wide policy |
The steering article also covers output styles and appending to the system prompt. I’d treat both as rare tools: a custom output style replaces the default software-engineering instructions unless you explicitly keep them, and adherence to appended instructions drops as you pile more on. If you’re reaching for these to fix a coding-convention problem, one of the mechanisms above is almost always the better home.
The most common anti-patterns
Most of these are the same mistake — a category error — wearing different clothes.
- “Never” rules in prose. “Never edit generated files” in CLAUDE.md is a hope. A
PreToolUsehook on the generated path is a fact. - The novel-length CLAUDE.md. Procedures, history, rationale, and every lesson ever learned, all loaded on every turn. Extract the procedures to skills, the path-specific bits to scoped rules, and delete anything that no longer fixes a real problem.
- Unscoped rules. A rule about database migrations that loads while someone edits CSS.
- A subagent for everything. The subagents article is explicit that too many custom agents make automatic delegation less reliable; most teams do fine with a handful of well-scoped ones.
- Vague delegation. “Explore the codebase” isn’t a task. “Find every call site of
X, list file and line, don’t edit” is. - Skills with vague descriptions. If the description doesn’t say when, the agent can’t decide whether to load it.
- Personal preferences in the team file. Now everyone’s sessions carry one person’s taste.
- Automating before the pattern is clear. Writing hooks and agents for a workflow nobody has done by hand three times yet. Start conversationally; codify once you know what you’d codify.
Where I’d be careful
- These are product specifics, and they move. File names, event names, frontmatter fields, and token figures here come from Anthropic’s articles at the time of writing and will change. The pattern — always-on memory, scoped rules, on-demand procedures, deterministic hooks, isolated workers — is the durable part. Other tools have their own analogues (an
AGENTS.mdfile, editor-specific rule files, and so on); I’d map the categories rather than assume the semantics match one-to-one. - Advisory isn’t useless. “Skills advise, hooks enforce” can be over-learned into “only hooks matter.” Most of what makes an agent good on your codebase is knowledge and procedure, which can only be advisory. Hooks are the backstop, not the strategy.
- Hooks are code with your permissions. A badly written hook is a new attack surface, and a hook config added by a teammate is code you’re about to run. Review accordingly.
- Your harness regresses. Every edit to CLAUDE.md, a skill, or a hook changes agent behavior. Without some form of evals on real tasks, you’re tuning by vibes. And all of it is still context engineering — the mechanisms are just different answers to when the model sees what.
How I’d roll it out
If I were setting up a team from scratch, in this order:
- CLAUDE.md first. Run
/init, then cut it hard: commands, layout, non-obvious conventions. Assign an owner. The working rule: when the agent makes the same mistake twice, add the correction — one line. - One hook. A
PostToolUseformatter. Then aPreToolUseblock on the paths that must never be touched without a human. Promote every “never” sentence in CLAUDE.md into a hook or permission. - Scope what’s partial. Move path-specific conventions into rules with
paths, and per-package conventions into subdirectory files. - Extract the first skills. Look for the procedures you keep pasting or that are bloating CLAUDE.md — release, migration, PR description. Write tight descriptions. Two or three good skills beat twenty speculative ones.
- Add two or three subagents. A read-only researcher, a read-only reviewer, maybe a verifier that runs the app and reports without fixing. Use them conversationally before you rely on automatic delegation.
- Package and govern. Once the setup is stable, bundle skills, agents, and hooks into a plugin so the team shares one version, and push must-hold policy into managed settings.
The career angle
There’s a quiet new skill emerging here: designing the harness. It’s not prompt writing. It’s closer to platform engineering — deciding which knowledge is ambient, which is on-demand, which is enforced, and which runs in isolation, then keeping that configuration reviewed, versioned, and tested like any other code. People who get good at it improve everyone’s agent sessions, and because the skill is about categories rather than syntax, it transfers to whatever tool comes next.
Going deeper
If you want to practice this on a real codebase, Claude Code Mastery covers the daily loop and the full steering toolkit — memory files, skills, hooks, and subagents — hands-on. AI-Native SDLC puts that harness inside a governed delivery lifecycle, with evals on the agent configuration itself. For the upstream discipline that makes all of this work better, see Spec-Driven Development Foundations and OpenSpec Mastery. And if you want to build your own agents with the same primitives, Claude Agent SDK is the next step.
Sources: Anthropic, Steering Claude Code: skills, hooks, rules, subagents and more; Using CLAUDE.md files; How to configure hooks; Skills explained; The complete guide to building skills for Claude; Subagents in Claude Code. This post is independent commentary; product details may have changed since publication, so check the originals. Related in this series: the AI-native SDLC, context engineering, evals, Claude Code in production.