Apprivoiser l'imprévisible
La ruée vers l’or
On est au début, et personne ne sait vraiment où ça va. Comme les pionniers de la ruée vers l’or, on avance à l’instinct, on teste, on se plante, on recommence. Les outils changent tous les mois, les certitudes de janvier sont les punchlines de mars.
Ce qu’on cherche tous en tant que développeurs, au fond, c’est de la prédictibilité. Savoir que si on fait X, il se passe Y — c’est pour ça qu’on construit des tests, des types, des linters, des CI. Et l’IA est l’antithèse de tout ça : probabiliste par nature, brillante la plupart du temps, mais à côté de la plaque juste assez souvent pour qu’on ne puisse pas fermer les yeux.
Le problème, c’est qu’on est en bout de chaîne. On ne contrôle ni le prompt système, ni l’entraînement du modèle, ni les choix d’architecture du harness. On consomme un modèle figé après l’entraînement, incapable d’apprendre de ses erreurs d’une session à l’autre, et la seule chose sur laquelle on a vraiment la main, c’est ce qui se passe autour.
L’ère des instructions
Les éditeurs d’outils IA (Anthropic, Cursor, OpenAI, Codex) ont tous convergé vers la même idée : donner aux développeurs des moyens d’infléchir le comportement de l’agent. Des fichiers de contexte (AGENTS.md, CLAUDE.md), des rules (.cursorrules), des skills, des commandes custom. Plus on documenterait les règles et le contexte du projet, mieux l’IA se comporterait.
Sauf que tout ça repose sur des instructions, et le budget est plus serré qu’on ne le croit. Au moment où j’écris, d’après HumanLayer, un LLM de pointe peut en suivre environ 150 à 200 avec une cohérence raisonnable, et le prompt système de Claude Code en consomme déjà ≈50. Et ces instructions consomment du budget en permanence, même quand elles ne sont pas pertinentes : « utilise pnpm » occupe du contexte y compris quand l’agent édite du CSS. Dans la pratique, les instructions se perdent : l’IA a une mémoire naissante entre les sessions, mais pas de conscience de ses oublis, et il n’est pas rare de la voir produire du code qui compile mais qui passe à côté de conventions qu’on lui a pourtant expliquées.
Ce que la recherche nous dit
L’intuition « plus d’instructions = meilleur résultat » est séduisante, mais la recherche suggère l’inverse.
SkillsBench (février 2026) montre que les skills auto-générées par le modèle dégradent les résultats, que les skills écrites à la main aident mais seulement jusqu’à 3, et que la documentation exhaustive fait pire que pas de documentation du tout.
Même constat pour les fichiers de contexte. ETH Zurich montre que les AGENTS.md générés par LLM réduisent le taux de réussite d’environ 3% tout en augmentant les coûts de 20%. Les fichiers écrits manuellement s’en sortent un peu mieux (+4%), mais les agents suivent les instructions trop littéralement. Et quand on supprime la documentation existante du repo, les fichiers de contexte deviennent soudainement utiles, ce qui prouve qu’ils ne font que dupliquer des informations déjà découvrables dans le code.
Vercel arrive à 100% de pass rate avec un AGENTS.md ultra-condensé (8 Ko), mais leur propre conclusion : « small wording tweaks produce large behavioral swings. » Fragile.
Le piège du string match
La méthode Ralph Wiggum, popularisée par Geoffrey Huntley, est un bon pattern pour gagner en prédictibilité. L’idée : faire tourner un agent en boucle, chaque itération repartant avec un contexte frais, le progrès étant persisté dans les fichiers et l’historique git.
while :; do cat PROMPT.md | claude-code ; done
Là où ça se casse, c’est la détection de complétion. L’implémentation par défaut demande à l’agent d’émettre un token (COMPLETE) et vérifie sa présence dans la sortie par un simple string match.
En trois mois d’utilisation, j’ai eu trois fois des boucles en erreur détectées comme réussies. Le LLM, confronté à un échec, écrit quelque chose comme « la tâche a échoué, donc je ne dois pas écrire COMPLETE« . Le token est dans la sortie. Le string match passe. La boucle s’arrête. Tout est vert. Rien ne marche.
Le LLM n’a pas désobéi. Il a raisonné, et son raisonnement contenait exactement le token qu’il essayait de ne pas émettre — un peu comme la théorie des processus ironiques de Wegner : « ne pense pas à un ours blanc » garantit qu’on y pense. Si la détection reposait sur un script déterministe (vérifier que les tests passent, que le build compile), ce bug n’existerait pas.
L’approche inverse : mon flow actuel
L’approche qui émerge ces derniers jours, c’est l’inverse de l’instinct : moins d’instructions, pas plus. Matt Pocock arrive à la même conclusion : garder le strict minimum dans les fichiers de contexte, et pour tout ce qui doit être garanti, utiliser des mécanismes déterministes.
Hooks are powerful because they’re deterministic. This isn’t « prompt the agent to remember to run tests. » It’s « tests run because the workflow requires it. »
Sur ce projet, chaque cas d’usage qui était une instruction est devenu un script :
| Instruction CLAUDE.md | Équivalent déterministe |
|---|---|
| « Formate ton code avec oxfmt » | Hook PostToolUse qui reformate chaque fichier modifié |
| « Utilise pnpm, pas npm » | Hook PreToolUse qui rejette les commandes npm |
| « Ne merge jamais une PR toi-même » | Hook PreToolUse qui bloque gh pr merge |
| « Mets à jour modifiedAt quand tu modifies un article » | Script pre-commit qui met à jour la date automatiquement |
| « Ne mets pas de co-authoring dans les commits » | Hook commit-msg qui supprime la ligne |
Claude Code expose des hooks de lifecycle, des scripts shell qui s’exécutent automatiquement quand l’agent fait certaines actions, avant (PreToolUse) ou après (PostToolUse).
« Formate ton code avec oxfmt »
Hook PostToolUse : se déclenche après chaque modification de fichier. Le matcher filtre les outils concernés, le exit 0 garantit que le hook ne bloque jamais l’agent même si le formatage échoue. L’IA n’a pas besoin d’y penser.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|NotebookEdit",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/format-on-save.sh",
"timeout": 30
}
]
}
]
}
}
jq -r '.tool_input.file_path // .tool_input.notebook_path' \
| xargs oxfmt --write --no-error-on-unmatched-pattern 2>/dev/null
exit 0
« Utilise pnpm, pas npm »
Hook PreToolUse : rejette la commande avant qu’elle s’exécute, exit code 2, message d’erreur explicite. L’agent reçoit le message et s’adapte. L’instruction « utilise pnpm » dépend de la bonne volonté du modèle. Le hook qui rejette npm install est un mur.
jq -r '.tool_input.command' \
| grep -qE '^npm\b|\bnpm install\b|\bnpm i\b' \
&& echo 'Blocked: use pnpm instead of npm.' >&2 \
&& exit 2
exit 0
« Ne merge jamais une PR toi-même »
Même mécanisme. Un merge par erreur, c’est réversible (on rebase), mais c’est le genre de nettoyage qu’on préfère éviter.
jq -r '.tool_input.command' \
| grep -q 'gh pr merge' \
&& echo 'Blocked: merging requires human approval.' >&2 \
&& exit 2
exit 0
« Mets à jour modifiedAt quand tu modifies un article »
Le pre-commit Husky n’est pas nouveau : linting, formatting, c’est standard. Ce qui est intéressant, c’est d’aller plus loin avec des scripts métier. Le script ne se déclenche que quand du vrai contenu a changé, pour éviter une boucle infinie avec sa propre modification.
for file in $(git diff --cached --name-only --diff-filter=M -- 'content/*.mdx'); do
content_changed=$(git diff --cached -U0 "$file" \
| grep '^[+-]' | grep -v '^[+-]\{3\}' \
| grep -v '^[+-]modifiedAt:' | head -1)
if [ -z "$content_changed" ]; then
continue
fi
if grep -q '^modifiedAt:' "$file"; then
sedi "s/^modifiedAt:.*$/modifiedAt: \"$TODAY\"/" "$file"
else
sedi "/^publishedAt:.*/a\\
modifiedAt: "2026-03-02"
" "$file"
fi
git add "$file"
done
Et l’IA dans tout ça ? C’est elle qui écrit les scripts sur mesure pour le projet.
« Ne mets pas de co-authoring dans les commits »
Piège : le Co-Authored-By: Claude est injecté par la configuration de Claude Code, pas par le modèle. Lui demander de ne pas le faire dans un CLAUDE.md marchera la plupart du temps, mais pas tout le temps. La solution est dans le settings.json, pas dans les instructions.
{
"includeCoAuthoredBy": false
}
Filtrer ce que le modèle voit
Il y a une catégorie moins évidente : non pas corriger le code ni bloquer une action, mais filtrer ce que le modèle voit. Les hooks PostToolUse peuvent réécrire la sortie d’un outil avant qu’elle entre dans le contexte. Tronquer un output de build de 20 Ko à 10 Ko, compresser un fichier de 500 lignes en signatures de fonctions, supprimer les blocs <system-reminder> qui s’accumulent au fil de la conversation. Chaque octet qui n’entre pas dans le contexte est du budget libéré pour le raisonnement. claude-warden pousse cette logique jusqu’au bout : gouvernance de tokens, troncature, compression structurelle, budgets par subagent, le tout en Bash et jq.
Ce qu’il reste dans les instructions
Tout n’est pas automatisable. Ma commande /commit reste un prompt (conventional commits, logique de batching, détection du scope) parce que c’est du jugement, pas de la vérification. L’IA doit décider si un changement est un feat ou un fix, si deux fichiers vont dans le même commit ou non, et aucun script ne peut faire ça.
La règle que j’applique :
- Répétable et vérifiable → script (hook, linter, pre-commit)
- Contextuel et nécessite du jugement → instruction (commande, skill)
- Dans le doute → script
Et surtout : pas de fichier de 200 lignes d’instructions que le modèle va survoler et qui deviendra avarié en quelques semaines. Le contenu du projet (le code, les types, la structure des fichiers) est déjà discoverable. L’IA sait lire un package.json, un tsconfig.json, une arborescence. L’expliciter dans un fichier de règles, c’est ajouter du bruit. Si j’ai un CLAUDE.md, c’est quelques lignes, aussi peu que possible, et uniquement pour des erreurs répétées que je n’ai pas réussi à extraire en script. L’exercice utile, c’est de relire chaque ligne de son fichier d’instructions en se demandant : est-ce qu’un script pourrait garantir ça ? Si oui, la ligne n’a rien à faire là.
On a un biais naturel à vouloir tout documenter pour l’IA, comme on le ferait pour un nouveau développeur. Mais un nouveau développeur lit la doc une fois et s’en souvient. L’IA la relit à chaque session, et chaque token de contexte occupé par nos règles est un token de moins pour le raisonnement.
Quand les instructions deviennent des portes
Il y a un cas où les instructions gardent un avantage sur les scripts : les tâches créatives en plusieurs étapes, où chaque phase nécessite du jugement, mais où la transition entre les phases peut être verrouillée.
J’ai construit un skill pour automatiser la création de composants React depuis Figma, en s’appuyant sur un design system et des design tokens. Le workflow est découpé en cinq étapes, inspiré de la méthode Ralph Wiggum :
- Étude : analyser les design tokens et le fichier Figma, extraire les variables, les espacements, la typographie
- Architecture : définir la décomposition en composants et sous-composants, les props, les variantes
- Implémentation : générer le code. Erratique by design, c’est une première passe, pas le résultat final
- Refinement loop : comparer le rendu au design, lister les écarts, corriger, re-comparer, jusqu’à zéro différence
- Validation : tests, accessibilité, cohérence avec le design system
Chaque étape a des conditions sine qua non pour écrire COMPLETE et passer à la suivante. L’étape 1 ne se termine pas tant que les tokens ne sont pas extraits. L’étape 4 ne se termine pas tant qu’il reste des écarts visuels. Ce sont des portes, pas des suggestions.
L’implémentation de l’étape 3 est volontairement lâche, et c’est contre-intuitif, mais plutôt que de demander à l’IA d’être parfaite du premier coup (ce qu’elle ne sera pas), j’accepte l’imperfection et je la corrige dans une boucle dédiée. L’étape 4 est le filet de sécurité de l’étape 3.
C’est un skill, un fichier d’instructions agnostique de l’outil. Ça marche avec Claude Code, Cursor, ou n’importe quel agent qui supporte les skills. Mais pour vraiment fiabiliser le processus, l’étape suivante serait de transformer chaque porte en un vrai script. Une boucle for à la Ralph Wiggum, où chaque itération relance l’agent avec un contexte frais sur une seule étape, et où la complétion est vérifiée par du code, pas par le LLM.
Le skill avec ses portes, c’est un compromis pragmatique. La boucle avec des vérifications déterministes, c’est la cible. Entre les deux, il y a le temps qu’on a et les outils qu’on maîtrise.
Conclusion
On est encore tôt, les outils changent tous les mois, et ce qui marche aujourd’hui sera peut-être obsolète demain. Même les parties prenantes ne savent pas trop où elles vont.
La documentation officielle de Claude Code recommande de lancer /init pour générer un CLAUDE.md, alors que la recherche montre que les fichiers générés par LLM dégradent la performance. Les bonnes pratiques de prompting sont passées en quelques mois de « mets des CRITICAL, NEVER, ALWAYS » à « sois positif, Claude 4 est plus sensible« . Tout en écrivant sur la même page qu’on peut « tune instructions by adding emphasis (e.g., ‘IMPORTANT’ or ‘YOU MUST’) to improve adherence. »
Quand on est en bout de chaîne, des algorithmes probabilistes ressemblent à des sciences inexactes. Notre levier, c’est le même que d’habitude : tester, observer, conclure. Mais le principe ne changera pas : la prédictibilité se construit avec du code, pas avec des mots.