claude code hooks, explained.
you can ask claude to run the formatter after every edit. most of the time it will. a hook makes it happen every single time.
claude code hooks are commands that run automatically at set points in claude's lifecycle, and the thing that makes them matter is one word: deterministic. an instruction in your CLAUDE.md is a strong suggestion. a hook is a guarantee. if something has to happen every time, without fail, it does not belong in a prompt. it belongs in a hook.
this is the same idea behind everything i teach about making ai actually work: the results come from a system, not from hoping the model behaves. hooks are that principle turned into config.
the five events
a hook fires on one of five moments. pick the moment, optionally scope it to certain tools, and give it a command to run.
| event | fires when |
|---|---|
| PreToolUse | before a tool call runs (can block it) |
| PostToolUse | after a tool call completes (format, log) |
| UserPromptSubmit | when you submit a prompt, before claude reads it |
| Stop | when claude finishes responding |
| Notification | when claude sends a notification |
the everyday one: auto-format after edits
the most common hook. set a PostToolUse hook with a matcher of Edit|MultiEdit|Write so it fires whenever claude changes a file. the command checks the file type and runs the right formatter: prettier for typescript, gofmt for go, whatever your project uses. now formatting is not a thing you remember to ask for. it just happens.
the powerful one: block dangerous actions
a PreToolUse hook runs before a tool executes and can stop it. your command gets the tool name and input as json on stdin, and the exit code decides what happens:
- exit 0: proceed normally.
- exit 2: block the action, and the stderr message is fed back to claude as feedback, so it understands why and can adjust.
- any other code: a non-blocking error shown to you, nothing stops.
this is how you turn a rule from "suggested" into "guaranteed." block writes to a production config folder. block a bash command containing rm -rf. block commits to main. i learned the value of this the hard way: the difference between a rule you hope holds and a rule the machine enforces is the difference between a good day and a very bad one.
share them with your team
hooks in .claude/settings.json are project-level, so you can check them into your repo and your whole team gets them automatically. use the CLAUDE_PROJECT_DIR variable in your commands so scripts resolve no matter what directory claude is working from. one file, and the whole team's guardrails are identical.
how this fits the bigger picture
prompts, skills, and memory shape what claude tends to do. hooks decide what claude is not allowed to skip. you want both: judgment for the open-ended work, and hooks for the rules that can never slip. that is the same operator mindset as keeping a human on the send, just automated.
the takeaway
if it needs to happen every time, do not put it in a prompt, put it in a hook. PostToolUse for formatting and logging. PreToolUse to block the dangerous stuff. configure it in settings.json, check it into the repo, and stop hoping.
want the systems side of ai, not just the tips?
the free system builder helps you set claude up on your real work, so the results come from a setup you own, not a prompt you retype. built in claude, today.
get the free system builderor just follow along. new field notes most weeks on x, instagram, and tiktok.