Can Hooks only Block things, or can they also actively add information into the conversation?
Yes, they can. Besides intercepting and blocking, Hooks can also actively inject information. For example, the SessionStart event fires at the beginning of every session, and you can bind a command that automatically pulls the current git branch name and feeds it in as extra background context right at the start — no need to manually tell Claude "here's which branch I'm on" every single time.
This is the same mechanism as the interception-style use of PreToolUse/PostToolUse, just applied in a different direction: interception is "block this from happening when it shouldn't," injection is "automatically supply this information whenever it should always be present." Both turn something that should reliably happen into a mechanical guarantee, rather than relying on a verbal reminder.
Why not just convert every rule in CLAUDE.md into a Hook and solve the "missed execution" problem once and for all?
Because the two are suited to different types of rules. A Hook's strength is rules with a clear condition and a fixed action — like "Block if this specific string is detected" or "run a formatter after a file write." These don't require understanding context or exercising judgment; they're purely condition-met-then-execute, which fits well into a script.
But some rules require judging things like "does this match the project's style" or "is this piece of code's naming clear" — rules that need semantic understanding, without an absolute right or wrong, and are very hard to encode into a simple shell script that makes that call. For these, leaving them in CLAUDE.md and letting Claude apply understanding actually fits better than forcing them into a hardcoded rule. In practice, the better division of labor is: mechanical, black-and-white rules go to Hooks, rules requiring semantic judgment stay in CLAUDE.md — the two work together rather than being an either/or choice.
Is setting up a Hook complicated — do I need a deep technical background?
The barrier is actually not that high. The most basic Hook is just a shell command plus the event type you're binding it to, written into a settings file. Take the most common case, auto-formatting: you just specify the "PostToolUse event," matching tools like "Write or Edit," and bind it to "the command that runs your formatter" — no complex program logic required. Many common scenarios can actually be handled by copying an example from the official docs or shared by the community and just tweaking the path and tool name.
What genuinely takes a bit more thought is the "interception-style" Hook — like checking whether a command contains a dangerous string — which does require writing a bit of conditional logic. But even that doesn't require a deep programming background; there are already plenty of ready-made examples online you can reference directly and adjust to your project's needs, rather than designing it from scratch.
If I set up a Hook and later change my mind — say I don't want formatting to run this one time — is it hard to work around?
That depends on how you designed it. For a formatting command bound to PostToolUse, you can usually still manually revert it afterward, or temporarily remove that Hook from the settings file and continue — a Hook guarantees "this runs when the condition matches," not "once it runs, no human can adjust it afterward." But for a blocking-style Hook bound to PreToolUse (like intercepting a deletion command), the whole point of that Hook existing is that it shouldn't be easy to bypass — it typically isn't designed with a convenient exception toggle either. If you genuinely need to run the action it blocked, you have to manually edit the settings file itself, not negotiate with Claude in conversation.
This is worth thinking through before you set up a Hook: do you want this rule to be "applied automatically most of the time, with occasional flexible adjustment," or "something that should never be bypassed no matter what"? The two mindsets should lead you to design the Hook with different levels of strictness.
You wrote "format code with Prettier after every change" in CLAUDE.md, and Claude follows it most of the time — but every so often it slips. This isn't Claude being lazy on purpose; CLAUDE.md is fundamentally an instruction. Claude tries to follow it, but it's still a line of text the model itself decides whether to act on, which is an entirely different level of reliability from a command written into a shell script that your computer guarantees will run. This is exactly the gap Hooks solve: they make a specific action happen at a specific moment for certain — not "Claude does it if it remembers," but "this happens regardless of what Claude decides."
A Hook is a shell command you define in a settings file, bound to a specific event in Claude Code's lifecycle — like "before a tool call," "after a tool call," or "when a session starts." When that event actually occurs, the command you've bound to it runs automatically, without Claude needing to actively decide whether to do it. The concept is similar to Git hooks: Git has fixed trigger points before and after a commit where you can attach your own scripts, and Hooks work on the same logic within Claude Code's operational lifecycle.
PreToolUse fires "before" Claude executes a tool, and can be used to Block dangerous operations — for example, detecting a deletion pattern like rm -rf in a command and blocking that execution outright before it actually runs. PostToolUse fires "after" a tool executes, and its most common use is auto-formatting: the moment Claude writes or edits a file, immediately and automatically run Prettier or whatever formatter you use, without needing to repeatedly remind Claude in CLAUDE.md to "remember to format."
A rule written in CLAUDE.md or a prompt can get skipped because the conversation ran long and the rule got diluted, or because Claude judges "this time it doesn't seem necessary" for whatever the task is. A Hook doesn't have this problem, because it never goes through Claude's judgment at all — the event fires, the command runs. This is a mechanical guarantee, not a persuasive request. That difference matters most in scenarios that must hold true every single time: protecting sensitive files from accidental overwrite, or making sure tests actually run after every change — these aren't things you want happening "most of the time," you want them happening "every time."
You don't need to learn every event type at once — starting with three of the most practical combinations is enough: auto-formatting (a PostToolUse hook bound to a formatter), protecting sensitive files (a PreToolUse hook intercepting writes to files like .env), and blocking dangerous commands (a PreToolUse hook that detects and stops deletion-type commands). These three cover the most common beginner pain point of "I definitely said this, and it still got missed." Once you're comfortable, add more as your own workflow calls for it.
Every time Claude misses a rule you thought was clearly stated, the cost isn't just that one slip — it's the extra conversation turns you spend discovering the problem, re-explaining the rule, and asking for a fix. All of that is real Token spend, and it's often discovered halfway through a project, exactly when the cost of correcting it is highest. Upgrading the rules that truly can't be compromised on — from "a request written in CLAUDE.md" to "a mechanism guaranteed by a Hook" — trades some upfront setup time for the long-term savings of never having to re-correct the same thing and burn conversation turns on it again.