Jacky Raimond.
DienstenServices BlogBlog OverAbout
Plan een callBook a call
/
Alle notesAll notes
AI·Note 007·11 jun 2026·6 min

Hooks, guardrails en het verschil — Claude Code-begrippen uitgelegdHooks, guardrails and the difference — Claude Code concepts explained

Een instructie in CLAUDE.md is een verzoek. Een hook is handhaving. En een deny-regel wint altijd, ook van een hook. Zo zit dat systeem in elkaar.An instruction in CLAUDE.md is a request. A hook is enforcement. And a deny rule always wins, even over a hook. Here is how that system fits together.

Wie met Claude Code werkt komt al snel drie begrippen tegen: CLAUDE.md, skills en hooks. Ze lijken op elkaar, maar ze doen heel verschillende dingen. En wanneer je wat gebruikt maakt een groot verschil.

CLAUDE.md, skills en hooks

CLAUDE.md laadt Claude automatisch in bij elke sessie. De plek voor alles wat altijd relevant is: architectuur en mappenstructuur, code style, build- en testcommands, workflow-instructies. Mijn vuistregel: houd het onder de 200 regels. Groeit het verder, verplaats referentiemateriaal dan naar skills. De belangrijkste beperking: het is advisory. Claude probeert het te volgen, maar kan het negeren.

Een skill laad je op aanvraag, of Claude laadt hem automatisch als hij relevant is. De plek voor terugkerende taken met een vast patroon: een Jira-ticket verrijken, een databasemigratie aanmaken, een deployment-workflow uitvoeren. Let wel: een skill werkt het best als de context klopt. Wat voor jouw architectuur werkt, werkt niet automatisch in een andere.

Een hook draait automatisch op een vast moment. Voor een prompt, na een tool-aanroep. Deterministisch, buiten het LLM om. Hier zit het cruciale verschil: "bewerk nooit het .env-bestand" in CLAUDE.md is een verzoek. Een PreToolUse-hook die die actie blokkeert is handhaving. Als een regel altijd moet gelden, maak er een hook van.

Guardrails: regels zonder code

Waar een hook programmatisch reageert op een lifecycle-event, zijn guardrails declaratieve regels die je vooraf configureert. Permissions schrijf je op in je configuratie via allow- en deny-lijsten. Geen code. Claude Code checkt dit automatisch voor elke tool-aanroep. Via glob-patronen stel je per tool en bash-command in wat automatisch wordt toegestaan, geblokkeerd of altijd om goedkeuring vraagt. Bijvoorbeeld: alle npm run-commands automatisch toestaan, maar git push altijd blokkeren.

De kern van guardrails is niet wantrouwen. Het is dat een goed systeem niet afhankelijk is van vertrouwen alleen. En test ze actief: een guardrail die je nooit test werkt misschien niet wanneer je hem nodig hebt.

In productie ben je zelf verantwoordelijk

Draait Claude niet als developer-tool maar als agent of orchestrator in je eigen applicatie, dan gelden dezelfde principes. Alleen ben jij verantwoordelijk voor de implementatie.

Via de Agent SDK heb je drie ingebouwde mechanismen. Met allowedTools definieer je exact welke tools een agent mag gebruiken. Met permissionMode kies je vooraf hoeveel autonomie hij krijgt, van plan (alleen read-only tools) tot bypassPermissions (alles toegestaan, alleen voor sandboxed omgevingen). En met de canUseTool-callback onderschep je elke tool-aanroep met eigen code. Daar bouw je approval flows waarbij een mens goedkeurt voordat een actie doorgaat.

Via de directe API bouw je guardrails zelf. Geef je een tool niet mee in de tools-parameter, dan bestaat hij niet voor het model. Het grote verschil met Claude Code: in productie is er geen interactief scherm, geen dialoogvenster dat om goedkeuring vraagt. Alles wat je niet expliciet regelt valt buiten jouw controle. Daarom ontwerp je dit vooraf en voeg je het niet achteraf toe.

Het verschil, en de volgorde

Hooks zijn programmatisch en context-bewust. Je schrijft logica en kunt input inspecteren, modificeren of blokkeren op basis van wat er precies gebeurt. rm -rf / blokkeren maar rm tmp/* toestaan? Een hook. Elk tool-gebruik loggen voor een audit trail? Een hook. Context toevoegen aan een prompt voordat Claude hem ziet? Ook een hook.

Guardrails zijn declaratief en structureel. Een regel in je configuratie, geen code. Bash(git push *) is geblokkeerd, Bash(npm run *) is toegestaan.

En dit is het deel dat weinig mensen weten: ze werken samen in een vaste volgorde. Eerst hooks. Dan deny-regels, en die winnen altijd, ook als een hook al "allow" heeft teruggegeven. Dan permission mode en allow-regels. Tenslotte de canUseTool-callback. Een hook kan een deny-regel dus nooit overschrijven.

In de praktijk gebruik je beide. Guardrails voor de harde grenzen: wat nooit mag, ongeacht de context. Hooks voor de slimme uitzonderingen: alles wat afhankelijk is van situatie, context of input.

If you work with Claude Code, you quickly run into three concepts: CLAUDE.md, skills and hooks. They look alike, but they do very different things. And knowing when to use which makes a big difference.

CLAUDE.md, skills and hooks

Claude loads CLAUDE.md automatically at the start of every session. It is the place for everything that is always relevant: architecture and folder structure, code style, build and test commands, workflow instructions. My rule of thumb: keep it under 200 lines. If it grows beyond that, move reference material into skills. The key limitation: it is advisory. Claude tries to follow it, but can ignore it.

A skill is loaded on demand, or Claude loads it automatically when it becomes relevant. It is the place for recurring tasks with a fixed pattern: enriching a Jira ticket, creating a database migration, running a deployment workflow. One caveat: a skill works best when the context fits. What works for your architecture does not automatically work in another.

A hook runs automatically at a fixed moment. Before a prompt, after a tool call. Deterministic, outside the LLM. This is where the crucial difference sits: "never edit the .env file" in CLAUDE.md is a request. A PreToolUse hook that blocks that action is enforcement. If a rule must always hold, turn it into a hook.

Guardrails: rules without code

Where a hook reacts programmatically to a lifecycle event, guardrails are declarative rules you configure up front. Permissions live in your configuration as allow and deny lists. No code. Claude Code checks them automatically before every tool call. Using glob patterns, you decide per tool and bash command what gets allowed automatically, what gets blocked, and what always asks for approval. For example: allow every npm run command automatically, but always block git push.

The core of guardrails is not distrust. It is that a good system does not depend on trust alone. And test them actively: a guardrail you never test might not work when you need it.

In production, you are on your own

When Claude runs not as a developer tool but as an agent or orchestrator inside your own application, the same principles apply. Except now you are responsible for the implementation.

With the Agent SDK you get three built-in mechanisms. allowedTools defines exactly which tools an agent may use. permissionMode sets how much autonomy it gets up front, from plan (read-only tools only) to bypassPermissions (everything allowed, sandboxed environments only). And the canUseTool callback intercepts every tool call with your own code. That is where you build approval flows in which a human signs off before an action runs.

With the direct API, you build guardrails yourself. If you do not pass a tool in the tools parameter, it does not exist for the model. The big difference with Claude Code: in production there is no interactive screen, no dialog asking for approval. Anything you do not explicitly arrange falls outside your control. Which is exactly why you design this up front instead of bolting it on afterwards.

The difference, and the order

Hooks are programmatic and context-aware. You write logic, and you can inspect, modify or block input based on what is actually happening. Block rm -rf / but allow rm tmp/*? That is a hook. Log every tool use for an audit trail? A hook. Add context to a prompt before Claude sees it? Also a hook.

Guardrails are declarative and structural. A rule in your configuration, no code. Bash(git push *) is blocked, Bash(npm run *) is allowed.

And this is the part few people know: they work together in a fixed order. Hooks first. Then deny rules, and those always win, even when a hook has already returned "allow". Then permission mode and allow rules. Finally the canUseTool callback. A hook can never override a deny rule.

In practice you use both. Guardrails for the hard boundaries: what is never allowed, regardless of context. Hooks for the smart exceptions: everything that depends on situation, context or input.

Migratie op de planning?Migration on the roadmap?
Begin met een Stack Audit.Start with a Stack Audit.
Plan een callBook a call