Mastering Claude Code — From Zero to 10x — 5. Set up a guardrail: hooks

17 min read min de lecture
Chapter 05

Set up a guardrail: hooks

Chapter 5 of 10 · 50%

Chapter objectives

  • Understand what a hook is and when it fires
  • Distinguish the 3 types of hooks
  • Build a quality gate that blocks non-compliant publications

The risk: publishing a mistake

Your skill writes and publishes. But what happens if a post contains an em dash you hate, exceeds the platform's character limit, or goes out to Instagram without an image? Right now, nothing stops it. You could proofread every post by hand — but then why automate at all? You could add "double-check before publishing" to the skill — but an instruction in a prompt remains probabilistic: the model follows it almost always, and "almost" isn't good enough when the mistake is public and instant.

You need a guardrail of a different nature: a check that runs systematically, that Claude can neither forget nor bypass. That's exactly what hooks are.

What is a hook?

A hook runs code at key moments of Claude Code's lifecycle. It fires automatically: you don't have to remember to call it, and Claude doesn't have to remember to respect it — it's imposed on it. A hook can format files after a modification, block a command before it runs, inject context at session start, or notify you when a task finishes.

The trigger moments (the "events") cover the whole lifecycle: before a tool runs (the PreToolUse event — the one we care about for blocking a publication), after it runs (PostToolUse — perfect for reformatting a modified file), when you submit your message, at session start, or when Claude finishes its reply. Each hook is tied to an event and, optionally, a filter (for example: only Bash commands containing "publish").

The crucial difference with a prompt instruction: an instruction is probabilistic (followed almost always), a hook is deterministic (executed always). For anything non-negotiable — quality, security, compliance — you need a hook, not a guideline.

The three ways to validate

Command hookRuns a shell script. Fast, deterministic, and free in tokens — but the logic must be simply programmable. Ideal for: character limits, banned words, missing files.
Prompt hookAn LLM reads the command and its context, understands the content natively, and returns a pass/fail decision. Ideal for fuzzy criteria: "is this post on-brand?"
Agent hookA multi-turn subagent that can read files, run commands, and reason through a multi-step validation. The most powerful and the most expensive: for complex checks.

The selection rule: take the least powerful one that suffices. Checking a character limit with an agent hook is bringing a bulldozer to plant a lettuce — slow, expensive, and more fragile. Our quality gate combines simple, objective rules: a command hook is the right tool.

Where hooks live

Hooks are configured in your settings files (.claude/settings.json to share them with the project, or settings.local.json for yourself only). The structure ties together an event, a filter ("matcher"), and the command to run. Here's the general shape of a hook that fires before Bash commands:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/hooks/quality-gate.js"
          }
        ]
      }
    ]
  }
}

The blocking mechanism is elegantly simple: the hook's script receives the action's details as input, and its exit code decides what happens next. Exit 0: all good, the action proceeds. Exit 2: the action is blocked, and whatever the script wrote to stderr is sent back to Claude — which reads it and fixes the issue. So the hook doesn't just say no: it explains why, and Claude uses that explanation to repair.

As with skills, you don't write this configuration by hand: you describe the guardrail you want and Claude creates the script and the configuration. But understanding the mechanism (event → script → exit code) lets you debug in two minutes instead of two hours.

Building the quality gate

We create a hook that fires before any publication and checks the content. If it fails, the command is blocked and Claude sees precisely what to fix:

PROMPT
create a command hook that fires before any Bash publishing command.

the hook checks the post's content:
- em dashes (to be replaced with "...")
- exceeding the platform's character limit
- missing media for platforms that require one
- banned words from my brand voice guide

if a check fails: block the command and show precisely what to fix.
flowchart TD
  P["Publication attempt"] --> H{"Hook: quality gate"}
  H -->|"Compliant"| OK["Published"]
  H -->|"Non-compliant"| KO["Blocked + list of fixes"]
  KO --> F["Claude fixes the content"]
  F --> P
The guardrail: nothing goes out until the content is compliant.

Notice the loop in the diagram: blocked doesn't mean dead. Claude reads the error message, fixes the content, and retries — most often without any intervention from you. The guardrail doesn't slow the system down: it makes it self-correcting.

Testing the hook (by forcing it to fail)

An untested guardrail is a decorative guardrail. The method: deliberately trigger every failure type and verify the block. It's the same reflex as a developer who tests their error cases, not just their happy path:

text
test the hook on a tweet that exceeds the character limit

test the hook on an instagram post without an image

test the hook on a post containing the word "revolutionary"

For each test, the output must show that the action was blocked ("validation failed") with the exact reason. If a case passes when it should block, now is when you find out — not the day a non-compliant post goes live. Perfect: your pipeline will never publish non-compliant content.

Beyond the quality gate

Once you've grasped the mechanism, hooks become a reflex for everything that must be systematic. A few classic uses: automatically reformatting every modified file (PostToolUse), forbidding any command touching a sensitive folder, loading the project's state at session start, or sending a notification when a long task finishes. The question to ask is always the same: "do I want this to happen every time, without depending on anyone's memory?" If yes, it's a hook.

For Lea, this chapter changes the nature of her system: before, the automation was fast but required her proofreading; now it's fast and trustworthy. That's what will make the scaling of the next two chapters possible — you only parallelize what you've first secured.

🛠️ Your turn

Context

Lea has banned the word "revolutionary" from her communication and always requires an image on Instagram — two non-negotiable rules she never wants to check by hand again. You set up the guardrail, then play the role of the mean tester: your goal is to get a non-compliant post through. If you can't, the guardrail is good.

Instructions

  1. Create the quality gate with the prompt provided in the chapter.
  2. Open the generated configuration in .claude/settings.json and spot the event, the matcher, and the script being called.
  3. Request an Instagram post without an image and verify it's blocked with an explicit message.
  4. Generate a post containing "revolutionary" and verify the block + the correction message.
  5. Force a Twitter character-limit overflow and watch the loop: block → fix by Claude → retry.
  6. Verify that a perfectly compliant post goes through without friction.
  7. Add a rule of your own (for example: ban multiple exclamation marks) and retest.
Hint — A command hook is the simplest choice for validating text content: objective rules, fast execution, zero tokens consumed.

In summary

  • A prompt instruction is probabilistic; a hook is deterministic: it runs every time, no exceptions.
  • Hooks attach to lifecycle events: before a tool (PreToolUse), after (PostToolUse), at session start…
  • 3 types: command (shell script), prompt (LLM decision), agent (multi-step validation) — take the least powerful that suffices.
  • The configuration lives in .claude/settings.json; an exit code of 2 blocks the action and sends the explanation back to Claude.
  • The quality gate blocks non-compliant publications and makes the pipeline self-correcting: Claude reads the error and repairs.
  • Test a hook by deliberately triggering every failure — an untested guardrail is decorative.
  • It applies to all your skills with no extra configuration, and it will also apply to the next chapter's subagents.

Quiz — check your understanding

1. What's the main characteristic of a hook?

A hook fires on its own (e.g. before a publication), without you having to call it.

2. Which hook type is the simplest for validating text?

A command hook is enough for deterministic content checks.

3. Why a hook rather than a "check before publishing" instruction in the skill?

For the non-negotiable (quality, security), you need a guarantee of systematic execution — that's the definition of a hook.

4. How does a command hook block an action?

Exit code 0: the action proceeds. Code 2: action blocked, and Claude reads the explanation to fix and retry.

5. Which event do you use to check content BEFORE it's published?

PreToolUse fires before a tool executes: it's the right moment to inspect and potentially block a publishing command.

Auteur(s)

R

REHOUMA Haythem

Haythem Rehouma est un ingénieur et architecte IA et cloud, formateur et enseignant technique, avec un profil orienté IA médicale, AWS, MLOps, LLM/RAG et vision par ordinateur.