Je bent een uur bezig met Claude Code. Het heeft een nieuw endpoint gebouwd, de validator aangepast, de mapping bijgewerkt. Alles ziet er goed uit. Je typt dotnet test en drie tests falen.
Niet omdat de code fout is. Maar omdat Claude een bestand opnieuw heeft geformateerd, een namespace heeft aangepast en een test-assertion brak die afhankelijk was van een specifiek stringformaat. Kleine dingen. Dingen die je meteen had gezien — als je na elke edit de tests had gedraaid in plaats van pas aan het eind.
Dit is precies het probleem dat hooks oplossen.
Wat zijn hooks?
Claude Code hooks zijn shell-commando’s die automatisch draaien op specifieke momenten tijdens een sessie. Je hoeft niet meer te onthouden dat je “draai de tests” moet vragen. Je configureert het één keer, en het gebeurt elke keer.
Er zijn vier typen hooks:
- PreToolUse — draait voordat Claude een tool uitvoert (zoals een bestand lezen of bewerken)
- PostToolUse — draait nadat een tool is uitgevoerd (perfect voor formatting)
- Stop — draait wanneer Claude een taak afrondt (ideaal voor tests)
- Notification — voor meldingen
Je configureert ze in .claude/settings.json. Klaar. Geen plugins, geen extensies, geen third-party tools.
De Stop hook: automatisch tests draaien
In een eerdere post schreef ik over het principe “vertrouw, maar verifieer altijd.” De Stop hook maakt van dat principe een automatische check.
Dit gebruik ik in mijn .NET-projecten:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "dotnet test --no-build --verbosity quiet 2>&1 | tail -5"
}
]
}
]
}
}
Elke keer dat Claude een taak afrondt, draait dotnet test. De --no-build flag slaat de buildstap over (Claude’s edit heeft in de meeste setups al een build getriggerd). De tail -5 houdt de output kort — je ziet alleen de samenvatting.
Als tests falen, ziet Claude de output en kan het direct fixen. Geen context switch. Geen handmatige stap. De feedbackloop is direct.
Het matcher-veld is hier leeg, wat betekent dat het bij elke Stop draait. Je kunt ook matchen op specifieke patronen als je het alleen in bepaalde situaties wilt triggeren.
PostToolUse: formatteren na elke edit
Als je in een team werkt met een .editorconfig of dotnet format-regels, ken je het verhaal. Iemands PR heeft formatting-wijzigingen door de logica-wijzigingen heen. De diff is onleesbaar. De reviewer is geïrriteerd.
Met een PostToolUse hook wordt elk bestand dat Claude aanraakt direct geformateerd:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "dotnet format --include \"$CLAUDE_FILE_PATH\" --verbosity quiet"
}
]
}
]
}
}
De matcher is hier "Edit|Write" — het triggert alleen wanneer Claude een bestand bewerkt of schrijft. Niet bij lezen of zoeken. De $CLAUDE_FILE_PATH-variabele geeft je exact het bestand dat is gewijzigd.
Dit betekent dat elk bestand dat Claude aanraakt wordt geformateerd volgens de regels van je team. Geen uitzonderingen. Geen “oeps, ik was vergeten de formatter te draaien.”
PreToolUse: bewaken voordat het gebeurt
PreToolUse hooks draaien voordat Claude iets doet. Zie ze als vangrails.
Een simpel voorbeeld: edits blokkeren op bestanden waar je niet wilt dat Claude aankomt.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "echo \"$CLAUDE_FILE_PATH\" | grep -q 'migrations/' && echo 'BLOCK: Do not edit migration files' && exit 1 || exit 0"
}
]
}
]
}
}
Als Claude iets probeert te bewerken in de migrations/-map, blokkeert de hook het. In een .NET-project met EF Core wil je absoluut geen AI-gegenereerde wijzigingen in je migratiebestanden.
Team vs. persoonlijk: settings.json vs. settings.local.json
Hier wordt het praktisch voor teams.
.claude/settings.json wordt ingecheckt in git. Elke developer in het team krijgt dezelfde hooks. De Stop hook die tests draait? Teambeslissing. De PostToolUse formatter? Teambeslissing. Je commit het, iedereen profiteert.
.claude/settings.local.json is persoonlijk. Staat niet in git. Hier zet je je eigen voorkeuren — misschien een notification hook die je een Slack-bericht stuurt, of een PreToolUse hook die past bij jouw persoonlijke workflow.
Het lokale bestand overschrijft het gedeelde bestand voor matchende hooks. Het team bepaalt de baseline, en jij kunt aanpassen voor je eigen setup.
Voor een .NET-team zou ik deze hooks in de gedeelde settings.json zetten:
- Stop hook: draai
dotnet testna elke taak - PostToolUse hook: draai
dotnet formatna elke edit - PreToolUse hook: blokkeer edits op migratiebestanden
Al het andere — notificaties, persoonlijke linting-voorkeuren, experimentele hooks — gaat in settings.local.json.
Eerlijk over beperkingen
Hooks zijn krachtig, maar het is geen magie.
Ze kosten tijd. Elke Stop hook voegt de uitvoertijd van dotnet test toe aan elke taakafronding. Voor een grote solution met trage tests kan dit frustrerend zijn. Gebruik --no-build en overweeg om alleen snelle unit tests te draaien, geen integratietests.
Het zijn shell-commando’s. Als je commando stil faalt of onverwacht een non-zero exit code teruggeeft, wordt het verwarrend. Test je hook-commando’s eerst handmatig.
Ze vervangen geen code review. Een groene test suite betekent niet dat de code goed is. Het betekent dat de code bestaand gedrag niet breekt. Architectuurbeslissingen, naamgeving, leesbaarheid — dat is nog steeds jouw verantwoordelijkheid.
Matcher-patronen zijn simpel. Het matcher-veld matcht op toolnamen, niet op bestandspatronen of projectnamen. Voor meer controle moet je de logica in je shell-commando afhandelen.
Begin met één hook
Je hoeft niet alles tegelijk in te richten. Begin met de Stop hook die je tests draait. Leef er een week mee. Je zult merken dat Claude zijn eigen fouten vaker opvangt, en dat je feedbackloop krimpt van “einde van de sessie” naar “einde van elke taak.”
Voeg daarna de formatter toe. Dan de vangrails.
Het doel is niet om alles te automatiseren. Het doel is om de dingen te automatiseren die je steeds vergeet — zodat je je kunt richten op de dingen die echt jouw oordeel nodig hebben.