Hooks for Deterministic Behavior
Why hooks exist to enforce hard requirements that prompted instructions can't reliably guarantee, and how lifecycle events work.
A hook is a shell command that fires automatically when a specific lifecycle event occurs during a Claude Code session — for example, before a tool runs, after a tool runs, or when the session stops. Hooks are configured in settings.json and execute deterministically: the shell command runs every single time the event fires, with no judgment call and no possibility of being skipped because Claude "forgot" or reasoned its way around it.
Why hooks exist
The core justification the exam wants you to internalize: prompted instructions are probabilistic, hooks are deterministic. If you put "please always run the linter before committing" in CLAUDE.md, that's a strong nudge, and most of the time Claude will follow it. But it's still a natural-language instruction being interpreted by a model — under unusual circumstances, competing instructions, or a long context window, it can be skipped. For anything that constitutes a hard requirement — something that must happen every time, no exceptions — a hook removes the ambiguity, because it's the harness executing a fixed shell command, not the model deciding whether to comply.
This is the distinction to hold onto: CLAUDE.md and prompts shape what Claude is inclined to do; hooks shape what unconditionally happens. If a requirement can tolerate occasional misses, a strongly worded instruction may be sufficient. If it cannot — for example, "never allow a commit that fails the test suite" — that belongs in a hook, not a hope.
Common lifecycle events
- PreToolUse: fires before a tool call executes — useful for validating or blocking an action before it happens (e.g., rejecting a Bash command that matches a dangerous pattern, even one permissions didn't catch).
- PostToolUse: fires after a tool call completes — useful for automatically running a formatter or linter right after a file edit, without relying on Claude remembering to do it.
- Stop: fires when Claude finishes responding — useful for enforcing "run the full test suite before ending the turn" as an unconditional check.
Hooks receive information about the event and can, depending on the event and exit behavior, block the action, inject feedback, or simply log and continue. The key exam point isn't memorizing every event name — it's recognizing that hooks are the deterministic enforcement layer, distinct from the persuasive layer (CLAUDE.md, prompts, skills).
Scenario: A team keeps finding that Claude occasionally commits code with an unformatted file, despite CLAUDE.md explicitly saying "always run the formatter before committing." What's the fix? Replace or supplement the instruction with a PostToolUse hook that runs the formatter automatically after every file edit, or a PreToolUse hook on the commit command that blocks if formatting is out of date. The reasoning: the team already tried the prompted-instruction approach and it's not reliable enough, which is exactly the signal that this requirement needs deterministic enforcement instead of a stronger-worded reminder.
Hooks versus permissions
Hooks and permission rules can look similar on the surface — both can block an action — but they solve different problems. A deny permission rule stops a specific tool call from happening at all. A hook can do that too (a PreToolUse hook that exits with a blocking status), but hooks can also run arbitrary logic beyond a simple allow/deny check: validating file contents, checking external state, or injecting extra context back into the conversation. Think of permissions as a fixed rulebook keyed on tool and command pattern, and hooks as a more general enforcement mechanism that can execute real logic at defined points in the lifecycle, including logic that has nothing to do with permissions at all, like automatically formatting a file after every edit.
Try it
Add a PostToolUse hook in your project's settings.json that runs your formatter (or even just echo a message) after any file-editing tool is used. Trigger it by asking Claude to edit a file, and confirm the hook fires every time regardless of what else is happening in the session — that unconditional firing is the entire point.