A CLAUDE.md instruction is a prompt — Claude reads it and decides how to apply it, which means in a long session it can be forgotten, deprioritized, or misapplied. A hook is different: it's a real script that runs automatically on a specific event, outside the model's interpretation entirely. It can't forget, and it can't talk itself out of running.
CLAUDE.md works well for conventions and context — things where you want Claude to use judgment. It's the wrong tool for a rule that must hold every single time, because a prompt is still just a prompt: in a long session with a lot of context, an instruction from the top of the file can get less weight than something discussed five messages ago. A hook doesn't have that problem, because it isn't part of the conversation at all — it's code that runs whether or not the model "remembers" to check.
Say a client project has one legacy config file you're deliberately not touching yet — refactoring it is a separate, scoped piece of work you haven't started. Instead of writing "don't touch legacy-config.js" in CLAUDE.md and hoping it holds for the whole session, a hook that runs before file edits can check the target path and exit with an error if it matches the protected file. The edit simply doesn't happen — no judgment call involved.
Rather than asking Claude to "remember to run the linter after changes," a hook that fires after file edits can run the linter automatically and surface the result. This turns "please remember to lint" from a hope into a guarantee — it happens every time, regardless of how long the session has run or how many other things are being discussed.
#!/bin/bash
# Example: a post-edit hook that runs a linter and fails loudly
# if the edited file doesn't pass — read your own hooks config
# for the exact event name and input format Claude Code provides.
npx eslint "$CLAUDE_EDITED_FILE" || {
echo "Lint failed on $CLAUDE_EDITED_FILE — fix before continuing."
exit 1
}
Treat the exact variable names and event payload as something to confirm against Claude Code's own hooks documentation for your installed version — the concept (a script gated on a real event) is the durable part; the precise interface details change between releases.
The rules worth enforcing as hooks are the ones where a mistake is expensive: never commit directly to a client's main branch, never touch a payments-related file without a second look, never skip the test suite before marking something done. These are exactly the rules that are easy to state once and easy to forget by message thirty of a long session — which is precisely the gap a hook closes.
Hooks, subagents, MCP, and Skills all solve different problems — the free guide covers the fundamentals first if you're still getting oriented.
Get the Free Guide →CLAUDE.md is a prompt — the model reads it and decides how to apply it, which means it can be forgotten, misread, or deprioritized in a long session. A hook is a real script that runs automatically on a specific event, outside the model's interpretation entirely. It cannot be talked out of running.
Common trigger points include before a tool runs, after a tool runs, and around user prompt submission — letting you intercept an action before it happens or react to one right after. The exact set of available events is documented in Claude Code's own hooks reference; treat this as the concept, not the full list.
Yes — a pre-action hook can inspect what's about to happen and exit with a failure to stop it, which is exactly how you'd enforce a rule like "never edit this file" as code instead of as an instruction the model might not apply consistently.
No — the most useful hooks on client work tend to be small: a linter that runs after every edit, a guard that blocks writes to one protected path, a script that logs every command run for a same-day changelog. Small and specific beats clever and broad.