Claude Code Hooks vs Skills vs Subagents: Three Ways to Extend the Agent, and When Each Backfires
The first time I tried to make Claude Code "always run the linter," I wrote a Skill. It worked most of the time. The rest of the time, Claude decided the task didn't need linting and skipped it. I spent an embarrassing afternoon tightening the Skill's description before I understood the actual problem: I had picked the wrong mechanism. "Always do X" is a Hook. "Decide whether to do X" is a Skill. They are not interchangeable, and the docs at the time were happy to let me confuse them. Claude Code now ships four ways to extend the agent: Hooks, Skills, Subagents, and the Agent SDK. They overlap enough to look like alternatives and differ enough that using one where another belongs costs you reliability, tokens, or both. I've built real harnesses on the first three, and read the SDK docs closely enough to know where it takes over. Here is how each one actually fires, what it costs you in context, and the specific way each one bites back. The one-line mental model Before the table, the version I tell teammates: Hooks are deterministic. They fire on an event whether Claude likes it or not. Skills are probabilistic. Claude reads a description and decides to pull them in. Subagents are isolation. They run in their own context window and hand you back a summary. The Agent SDK is you taking the wheel: the same agent loop, running in your own process. If you remember nothing else: Hooks remove a decision from the model, Skills add one, Subagents quarantine one. The comparison Mechanism How it fires Token cost Best for When it backfires Hooks Lifecycle events ( PreToolUse , Stop , etc.) defined in settings.json . Not the model's choice. Runs as a shell command outside the context window. Near zero, unless you deliberately inject stdout. Non-negotiable rules: format on write, block secret files, run tests before stop. A hook exiting 2 blocks the action. A bad Stop hook loops the agent forever. Skills Claude matches your prompt against the Skill's description , or you type /nam