J'ai Créé 11 Hooks de Code Claude. Six Ont Échoué en 24 Heures.

8 min read

J'avais un seul hook sur Claude Code. Un seul. Un vérificateur TypeScript qui lançait npx tsc dans un projet qui utilise Bun, avec un pipe vers head -20. Donc quand il y avait 12 erreurs, j'en voyais trois. Système au top.

Pendant ce temps, mon agent pouvait faire un force-push sur main, leaker mes clés API, et rm -rf le repo sans que rien ne l'arrête. Là où j'ai pété un câble, c'est quand il a déployé en prod sur le SaaS. Je lui avais explicitement dit de ne pas le faire. Il l'a fait quand même.

J'écris sur les bonnes pratiques Claude Code. Ma propre config n'en avait aucune.

Alors j'ai fait ce que tout développeur fait quand il réalise que son filet de sécurité est en carton : j'ai construit onze hooks d'un coup. Trois jours plus tard, six étaient désactivés. Les cinq autres, j'avais oublié qu'ils existaient. C'est exactement comme ça que ça doit marcher si vous voulez coder (sans relire votre propre code) et dormir tranquille. Presque.

TLDR : Les hooks qui survivent sont ceux qu'on oublie (sécurité, secrets, opérations destructrices). Ceux qui meurent sont ceux qui vous harcèlent à chaque modification. J'ai audité les miens en trois jours, tué 6 sur 11, et les 5 survivants ont déjà attrapé 2 quasi-leaks et un force-push. Auditez les vôtres ce soir. Ça prend 20 minutes.

Bande dessinée montrant un développeur débordé à un bureau encombré avec des boîtes d'alarme, puis un héros confiant avec un écran propre et des boucliers de sécurité, style satirique années 1970
Construire des intégrations comme un fondateur de startup : 11 hooks, 6 victimes, zéro survivant.

Le Hook Unique Qui Était Censé Me Sauver

Avant l'audit, toute ma config de hooks tenait en un seul trigger PostToolUse sur les opérations Edit et Write. Il lançait npx tsc pour vérifier les erreurs TypeScript après chaque modification de fichier. Deux problèmes avec ça.

Primo, le projet tourne sur Bun. Lancer npx dans un projet Bun, c'est comme demander à votre voisin français de relire vos devoirs de japonais. Ça marche à peu près. Parfois. Quand les étoiles s'alignent. Secundo, je pipais vers head -20, ce qui coupe la sortie. TypeScript gueule sur 12 types cassés, je vois les 3 premiers, Claude voit les 3 premiers, et on hoche tous les deux la tête avant de passer à autre chose. Mon vérificateur de types ne vérifiait pas les types.

Mais c'est ça le plus drôle. Ce hook cassé était la seule chose qui se dressait entre mon agent et un accès total à tout. Aucune protection sur git push --force. Aucun blocage sur rm -rf. Aucun scan de secrets. Aucune protection de déploiement. L'agent pouvait niquer le repo, pusher sur main, et leaker toutes les clés API du projet. La seule chose qu'il ne pouvait pas faire (en théorie), c'était écrire du TypeScript qui ne compile pas. Et même ça, il le faisait mal.

J'avais structuré mon CLAUDE.md comme un vrai fichier de config, avec des règles, des limites, quatre lignes de principes d'intégrité. Mais CLAUDE.md, c'est une suggestion. L'agent le lit, y réfléchit, et parfois décide qu'il sait mieux. Un hook, c'est un mur. Exit code 2, appel d'outil bloqué, pas de négociation.

Le mur existait. Il était juste cassé.

Mon filet de sécurité était un vérificateur de types qui ne pouvait pas vérifier les types.

5 000 Likes pour Zéro Garde-Fou

Le 31 mars, quelqu'un a publié un fork de Claude Code. Le code source avait leaké via le package npm quelques jours plus tôt (Fortune et CNBC l'ont couvert, c'est du domaine public). Le fork a supprimé les garde-fous de sécurité des prompts, retiré la télémétrie, débloqué les fonctionnalités expérimentales, et l'a uploadé sur IPFS. Ça a eu environ cinq fois plus de traction qu'un thread éducatif solide sur le cycle de vie des hooks qu'un développeur nommé Akshay avait posté la même semaine.

Ce ratio vous dit quelque chose, et ce n'est pas que les développeurs détestent la sécurité.

Les développeurs détestent le bruit.

Réfléchissez-y. Vous installez un hook de lint qui se déclenche à chaque modification de fichier. Vous installez un test runner qui ajoute 4 à 8 secondes par sauvegarde pendant un refactor de 15 fichiers. Vous installez un avertissement de contexte qui pop toutes les 20 minutes pour vous dire que la conversation devient longue (oui, je sais, c'est moi qui l'ai). Après une journée de ça, vous commencez à ignorer toutes les sorties de hooks. Toutes les notifications. Tous les avertissements. Y compris les vrais.

Le problème du détecteur de fumée de cuisine. Le truc bipe chaque fois que vous faites des toasts. Alors vous arrachez la pile. Et la seule nuit où il y a un vrai incendie, pas de détecteur.

C'est de ça que parle cet écart de traction. Pas "on ne veut pas de garde-fous". Plutôt "on ne veut pas de vos garde-fous, ceux qui nous interrompent 47 fois par jour pour ne rien dire d'utile."

La conclusion du projet free-code est fausse, cependant. Zéro garde-fou n'est pas la réponse. La réponse, c'est tuer les six qui font du bruit et garder les cinq qui vous sauvent.

Je n'ai pas utilisé free-code moi-même. Je lis ces chiffres comme un signal de marché, pas un endorsement. Mais le signal est clair : quand vous forcez les développeurs à choisir entre sécurité bruyante et silence, ils choisissent le silence à chaque fois.

3 Jours, 11 Hooks, 6 Body Bags

Je vous épargne le walkthrough hook par hook. (De rien.) Ce qui compte, c'est le pattern, et il était évident dès le deuxième jour : les hooks qui ont survécu sont invisibles, ceux qui sont morts étaient bruyants.

Les cinq qui ont vécu :

Le vérificateur TypeScript était un fix de 30 secondes. Remplacer npx par bunx, remplacer head -20 par tail -5 (vous voulez les dernières erreurs, pas les premières, parce que les premières sont généralement du bruit en cascade d'une vraie casse). A immédiatement attrapé une erreur de type que Claude avait introduite trois modifications plus tôt.

Le garde git était celui que j'aurais dû construire en premier. PreToolUse sur Bash, pattern-matching contre push --force, push.*-f, et reset --hard. Premier jour, il a bloqué un vrai force-push. Claude essayait de réparer un conflit de merge en forçant le remote. Si ça avait passé, ça aurait effacé un commit dont j'avais vraiment besoin. J'ai mergé les patterns d'opérations dangereuses dans ce même hook (rm -rf, chmod 777, curl | bash). Un hook, une liste de regex, toutes les opérations où "oups" est permanent.

Le scanner de secrets tourne en PreToolUse sur Edit et Write. Regex pour les patterns de clés API, bearer tokens, credentials encodés en base64. En trois jours, il a flaggé deux fois : un bearer token hardcodé dans un fichier de test, et un console.log qui dumpait un header d'auth complet. Les deux auraient été committés sans un bruit. Ce sont le même genre de secrets que j'avais trouvés assis en cleartext dans .claude/settings.local.json quelques semaines plus tôt. Le scanner les attrape avant qu'ils touchent le repo.

Le garde de déploiement et le logger d'appels MCP complètent les cinq. Garde de déploiement : zéro trigger en trois jours, ce qui est tout l'intérêt (on ne juge pas une ceinture de sécurité sur la fréquence de ses déclenchements). Logger MCP : a révélé que Claude fetchait la même ressource quatre fois par conversation. Pas un outil de sécurité, un diagnostic de fenêtre de contexte. Les deux restent parce qu'ils coûtent zéro friction.

Les six qui sont morts :

Bruit. Trois d'entre eux. Le test runner ajoutait 4 à 8 secondes par sauvegarde de fichier. Pendant un refactor touchant 15 fichiers, ça fait deux minutes à fixer le terminal. Désactivé après une demi-journée. Le hook de lint était redondant avec le vérificateur TypeScript (mêmes erreurs, format différent, double interruptions). L'avertissement de fenêtre de contexte se déclenchait toutes les 20 minutes. Après la dixième fois, j'étais entraîné à ignorer toutes les sorties de hooks. Ce dernier est la chose la plus dangereuse qu'un hook puisse faire.

Inutilité. Deux d'entre eux. Le vérificateur de taille de bundle mesurait le mauvais répertoire pour le mauvais outil de build. L'enforcer de messages de commit rejetait les messages "génériques", mais Claude écrit déjà des messages de commit décents par défaut. Résoudre un problème qui n'existe plus.

Redondance. Un. Un hook de commandes dangereuses séparé qui matchait les mêmes patterns que le garde git. Même regex, même exit code, deux hooks faisant un boulot. Mergé et supprimé.

Six down. Cinq debout.

Un Bon Hook Est Un Hook Qu'On Oublie

Voici ce qui sépare les survivants des morts : les survivants ne m'ont jamais fait penser à eux. Ils tournent à chaque appel d'outil, vérifient ce qu'ils vérifient, restent silencieux quand tout va bien. Je ne les remarque que quand ils bloquent quelque chose. Et quand ils bloquent quelque chose, c'est toujours quelque chose que je suis content qu'ils aient bloqué.

Les hooks morts faisaient l'inverse. Ils interrompaient, ralentissaient, harcelaient. Et après assez de ça, je les ai désactivés. C'est là que le vrai problème commence.

Un hook désactivé est pire que pas de hook. Parce que vous pensez toujours être protégé. Le scanner de secrets tourne, non ? Eh bien non, vous l'avez éteint mardi dernier quand vous en avez eu marre du bruit de lint, et vous avez oublié que le scanner était dans la même config.

C'est le mode de défaillance dont personne ne parle. Les mauvais hooks ne font pas que foirer. Ils empoisonnent les bons. Un hook bruyant vous entraîne à ignorer toutes les sorties de hooks, et ça inclut celui qui vient d'attraper vos credentials de base de données de production dans un console.log.

Il y a une échelle à ça. Les quatre lignes d'intégrité dans mon CLAUDE.md ("Never lie, Never hide, Never conceal, Never fail silently") ont changé le comportement de l'agent, mais elles restent des suggestions qu'un agent sous pression peut contourner. Quand l'agent optimise pour "terminé", il traite les règles CLAUDE.md comme consultatives. Un hook ne négocie pas. Exit code 2, opération bloquée. Point.

Mais les hooks ne remplacent pas CLAUDE.md. Ils couvrent les opérations où l'échec est irréversible : un secret leaké, un force-push, un déploiement accidentel. Pour tout le reste (style de code, décisions d'architecture, conventions de nommage), CLAUDE.md est le bon outil. On ne construit pas un mur de briques pour enforcer le nommage des variables.

Les choses qui ont besoin d'un mur, par contre, ont vraiment besoin d'un mur.

L'Écart

Avant l'audit : un hook. Cassé. Zéro sécurité. L'agent pouvait déployer, supprimer, et leaker sans faire un bruit.

Après : cinq hooks. Fonctionnels. Couvrant les trois choses qui comptent vraiment (sécurité des types, secrets, opérations destructrices). Silencieux quand tout va bien. Bruyants quand ça ne va pas.

L'écart entre "j'écris sur les bonnes pratiques Claude Code" et "je suis les bonnes pratiques Claude Code" faisait exactement quatre hooks de large.

À quoi ressemblent les vôtres ?

Si vous auditez vos propres hooks après avoir lu ça, je veux savoir ce que vous trouvez. Les pires découvertes font les meilleures histoires 😅

Sources

Thread d'Akshay sur le cycle de vie et la configuration des hooks Claude Code (mars 2025, X/Twitter).

(*) La couverture est générée par IA. Les hooks dans l'image ont l'air propres parce qu'ils n'ont jamais vu une vraie codebase.