Root Cause First
Effort: free — pure discipline d'enquête qui, la plupart du temps, réduit le coût tout court : une sonde décisive remplace le tir de tout le pipeline pour voir ce qui se passe. Élimine : les rustines sur la mauvaise chose — le correctif de symptôme qui cache le vrai bug et casse un consommateur en aval.
Pas de fix sans enquête. Un patch fait avant de comprendre l'échec corrige la
mauvaise chose, cache le vrai bug, et casse quelque chose en aval. Ton produit
n'est pas un patch — c'est une cause racine prouvée par une sonde décisive, et
un fix prouvé sans régression.
Deux lois gouvernent tout ce qui suit :
- Aucune supposition — le code, les données et le système live sont la
vérité ; les notes ne sont que des indices. Un commentaire, un souvenir,
une conclusion passée, même ta propre dernière phrase est une hypothèse tant
qu'une sonde ne l'a pas confirmée. Les mots « tous / chaque / aucun »
déclenchent une vérification en trois points : l'environnement, une
recherche sur tout le repo, et un scan de chaque appelant.
- Un contre-exemple vérifié tue la conclusion précédente immédiatement.
Quand une sonde contredit ce que tu croyais, dis simplement « je me suis
trompé — en fait c'est X », puis repars du fait nouveau. Ne maquille jamais.
La boucle (dans l'ordre ; ne saute rien)
- Lis l'erreur. Énonce le symptôme en une phrase précise. Lis le vrai
message, pas ce que tu t'attends à y lire. Nomme le rayon d'impact : qu'est-ce
qui dépend de la chose que tu soupçonnes ?
- Reproduis. Fais survenir l'échec à la demande — en live, ou dans un test
en échec. Chronomètre-le. Un « échec » qui revient en millisecondes quand
le vrai travail prend des secondes, c'est une exception avalée tôt, pas du
vrai travail qui échoue. L'écart de timing est lui-même un indice.
- Vérifie les changements récents. Diffe ce qui a changé depuis que ça
marchait — code, config, environnement, dépendances. Si l'historique est
long, bissecte-le.
- Cartographie les consommateurs. Pour un bug dans une surface partagée,
liste chaque appelant et comment chacun l'utilise (comparaison de chaîne
exacte ? booléen ? liste ?). La vraie régression se cache d'habitude dans
une comparaison exacte en aval, pas dans le bouton que tu tournes.
- Instrumente les frontières. Logge ou sonde à chaque couture de
composant — ce qui entre, ce qui sort. Remonte la mauvaise donnée en
arrière, frontière par frontière, jusqu'à atteindre la source. Fixe la
source, jamais le symptôme.
- Cause racine par hypothèse. Formule une hypothèse falsifiable. Trouve LA
sonde décisive qui la sépare des alternatives, et lance uniquement
celle-là. Ne tire pas tout le pipeline « pour voir ce que ça donne ».
- Fixe chirurgicalement, à la bonne couture. Le plus petit changement qui
résout la cause racine. Préfère la source partagée unique (un normaliseur,
un runner) à l'édition de N points d'appel. Quand c'est possible, rends le
fix inerte sur le chemin qui marche — il n'y change prouvablement rien et ne
s'active que sur le chemin cassé. Aucun refactor voisin embarqué.
- Prouve-le. Écris le test en échec qui reproduit le bug ; regarde-le
passer au rouge ; fixe ; regarde-le passer au vert. Puis lance les tests de
chaque chemin consommateur cartographié à l'étape 4 — le vert là-bas est ton
plancher de zéro régression. Une suite qui mocke la couture exacte qui a
échoué ne prouve rien.
- Vérifie en live. Pilote le vrai système — vraies requêtes, vraie base de
données, vrais logs. Jamais un script sidecar qui importe le code dans ton
propre process. Capture les preuves avant/après.
- Apprends. Note le symptôme, la sonde décisive, la cause racine, et
l'anti-patron qui l'a cachée, pour que le prochain bug de cette forme coûte
moins cher.
Construis la boucle de reproduction AVANT de théoriser
Si tu te surprends à lire du code pour bâtir une théorie avant qu'une commande
capable de virer au rouge existe — stop. Pas de commande capable de rouge, pas
de théorie. Un signal serré passe/échoue qui vire au rouge sur CE bug est le
plus gros levier de débogage qui soit. Investis un effort disproportionné ici.
Façons d'en construire une, en gros dans l'ordre : un test en échec ; un script
HTTP contre un serveur de dev ; un run CLI avec une entrée fixture, diffé
contre un instantané connu bon ; un script de navigateur headless ; une vraie
charge utile capturée, rejouée dans le chemin de code en isolation ; un harnais
jetable qui appelle une seule fonction ; une boucle de fuzz sur des entrées
aléatoires ; un harnais de bissection pour que le bisect automatisé marche ;
une boucle différentielle (même entrée dans l'ancienne et la nouvelle version,
diff des sorties).
Puis resserre-la : plus rapide (cache le setup, réduis la portée), plus
tranchante (asserte le symptôme précis, pas « ça n'a pas crashé »),
déterministe (fige le temps, seede le RNG, gèle le réseau). Une boucle
déterministe de deux secondes est un super-pouvoir.
Pour les bugs intermittents, vise un taux de reproduction plus haut, pas une
repro propre : boucle le déclencheur 100 fois, ajoute du stress, resserre les
fenêtres de timing. Un flake à 50 % se débogue ; un flake à 1 %, non.
Si tu ne peux vraiment pas construire de boucle, arrête-toi et dis-le. Liste ce
que tu as essayé et demande à ton humain un accès, un artefact capturé, ou une
instrumentation temporaire. Ne théorise pas sans boucle. Et si aucune couture
n'existe qui puisse répliquer le vrai schéma d'appel, cette absence EST un
constat — signale le trou d'architecture après l'atterrissage du fix.
Anti-patrons (comment les bugs coriaces restent en vie)
- Conclure d'une note ou d'un commentaire sans sonde.
- Fixer avant de reproduire.
- Croire une suite verte qui mocke la couture exacte qui échoue en live.
- Vérification sidecar — importer le code au lieu de piloter le système live.
- Changer un bouton de config sans cartographier les consommateurs à
comparaison exacte qu'il alimente.
- Des refactors larges embarqués avec un fix.
- Dire « tous / chaque / aucun » sans la vérification en trois points.
Fonctionne bien avec
Crédit d'échafaudage : Matt Pocock, diagnosing-bugs (mattpocock/skills). La composition et les règles dures ici sont BACKS AIOS.
1---2name: root-cause-first-43description: À utiliser face à un bug coriace, un échec silencieux, une chasse à la régression, ou un changement risqué qui pourrait casser en douce un consommateur en aval. Pas de fix sans enquête — lire l'erreur, la reproduire à la demande, vérifier les changements récents, instrumenter les frontières de composants, remonter le flux de données jusqu'à la source. Trigger words: debug, root cause, why is this failing, silent failure, regression, works in tests but fails live, systematic debugging, cause racine, pourquoi ça échoue, échec silencieux, régression, ça passe en test mais casse en live, débogage systématique.4license: MIT5---67# Root Cause First8**Effort:** free — pure discipline d'enquête qui, la plupart du temps, réduit le coût tout court : une sonde décisive remplace le tir de tout le pipeline pour voir ce qui se passe. Élimine : les rustines sur la mauvaise chose — le correctif de symptôme qui cache le vrai bug et casse un consommateur en aval.910Pas de fix sans enquête. Un patch fait avant de comprendre l'échec corrige la11mauvaise chose, cache le vrai bug, et casse quelque chose en aval. Ton produit12n'est pas un patch — c'est une cause racine prouvée par une sonde décisive, et13un fix prouvé sans régression.1415Deux lois gouvernent tout ce qui suit :16171. **Aucune supposition — le code, les données et le système live sont la18 vérité ; les notes ne sont que des indices.** Un commentaire, un souvenir,19 une conclusion passée, même ta propre dernière phrase est une hypothèse tant20 qu'une sonde ne l'a pas confirmée. Les mots « tous / chaque / aucun »21 déclenchent une vérification en trois points : l'environnement, une22 recherche sur tout le repo, et un scan de chaque appelant.232. **Un contre-exemple vérifié tue la conclusion précédente immédiatement.**24 Quand une sonde contredit ce que tu croyais, dis simplement « je me suis25 trompé — en fait c'est X », puis repars du fait nouveau. Ne maquille jamais.2627## La boucle (dans l'ordre ; ne saute rien)28291. **Lis l'erreur.** Énonce le symptôme en une phrase précise. Lis le vrai30 message, pas ce que tu t'attends à y lire. Nomme le rayon d'impact : qu'est-ce31 qui dépend de la chose que tu soupçonnes ?322. **Reproduis.** Fais survenir l'échec à la demande — en live, ou dans un test33 en échec. **Chronomètre-le.** Un « échec » qui revient en millisecondes quand34 le vrai travail prend des secondes, c'est une exception avalée tôt, pas du35 vrai travail qui échoue. L'écart de timing est lui-même un indice.363. **Vérifie les changements récents.** Diffe ce qui a changé depuis que ça37 marchait — code, config, environnement, dépendances. Si l'historique est38 long, bissecte-le.394. **Cartographie les consommateurs.** Pour un bug dans une surface partagée,40 liste chaque appelant et comment chacun l'utilise (comparaison de chaîne41 exacte ? booléen ? liste ?). La vraie régression se cache d'habitude dans42 une comparaison exacte en aval, pas dans le bouton que tu tournes.435. **Instrumente les frontières.** Logge ou sonde à chaque couture de44 composant — ce qui entre, ce qui sort. Remonte la mauvaise donnée en45 arrière, frontière par frontière, jusqu'à atteindre la source. Fixe la46 source, jamais le symptôme.476. **Cause racine par hypothèse.** Formule une hypothèse falsifiable. Trouve LA48 sonde décisive qui la sépare des alternatives, et lance uniquement49 celle-là. Ne tire pas tout le pipeline « pour voir ce que ça donne ».507. **Fixe chirurgicalement, à la bonne couture.** Le plus petit changement qui51 résout la cause racine. Préfère la source partagée unique (un normaliseur,52 un runner) à l'édition de N points d'appel. Quand c'est possible, rends le53 fix inerte sur le chemin qui marche — il n'y change prouvablement rien et ne54 s'active que sur le chemin cassé. Aucun refactor voisin embarqué.558. **Prouve-le.** Écris le test en échec qui reproduit le bug ; regarde-le56 passer au rouge ; fixe ; regarde-le passer au vert. Puis lance les tests de57 chaque chemin consommateur cartographié à l'étape 4 — le vert là-bas est ton58 plancher de zéro régression. Une suite qui mocke la couture exacte qui a59 échoué ne prouve rien.609. **Vérifie en live.** Pilote le vrai système — vraies requêtes, vraie base de61 données, vrais logs. Jamais un script sidecar qui importe le code dans ton62 propre process. Capture les preuves avant/après.6310. **Apprends.** Note le symptôme, la sonde décisive, la cause racine, et64 l'anti-patron qui l'a cachée, pour que le prochain bug de cette forme coûte65 moins cher.6667## Construis la boucle de reproduction AVANT de théoriser6869Si tu te surprends à lire du code pour bâtir une théorie avant qu'une commande70capable de virer au rouge existe — stop. Pas de commande capable de rouge, pas71de théorie. Un signal serré passe/échoue qui vire au rouge sur CE bug est le72plus gros levier de débogage qui soit. Investis un effort disproportionné ici.7374Façons d'en construire une, en gros dans l'ordre : un test en échec ; un script75HTTP contre un serveur de dev ; un run CLI avec une entrée fixture, diffé76contre un instantané connu bon ; un script de navigateur headless ; une vraie77charge utile capturée, rejouée dans le chemin de code en isolation ; un harnais78jetable qui appelle une seule fonction ; une boucle de fuzz sur des entrées79aléatoires ; un harnais de bissection pour que le bisect automatisé marche ;80une boucle différentielle (même entrée dans l'ancienne et la nouvelle version,81diff des sorties).8283Puis resserre-la : plus rapide (cache le setup, réduis la portée), plus84tranchante (asserte le symptôme précis, pas « ça n'a pas crashé »),85déterministe (fige le temps, seede le RNG, gèle le réseau). Une boucle86déterministe de deux secondes est un super-pouvoir.8788Pour les bugs intermittents, vise un taux de reproduction plus haut, pas une89repro propre : boucle le déclencheur 100 fois, ajoute du stress, resserre les90fenêtres de timing. Un flake à 50 % se débogue ; un flake à 1 %, non.9192Si tu ne peux vraiment pas construire de boucle, arrête-toi et dis-le. Liste ce93que tu as essayé et demande à ton humain un accès, un artefact capturé, ou une94instrumentation temporaire. Ne théorise pas sans boucle. Et si aucune couture95n'existe qui puisse répliquer le vrai schéma d'appel, cette absence EST un96constat — signale le trou d'architecture après l'atterrissage du fix.9798## Anti-patrons (comment les bugs coriaces restent en vie)99100- Conclure d'une note ou d'un commentaire sans sonde.101- Fixer avant de reproduire.102- Croire une suite verte qui mocke la couture exacte qui échoue en live.103- Vérification sidecar — importer le code au lieu de piloter le système live.104- Changer un bouton de config sans cartographier les consommateurs à105 comparaison exacte qu'il alimente.106- Des refactors larges embarqués avec un fix.107- Dire « tous / chaque / aucun » sans la vérification en trois points.108109## Fonctionne bien avec110111- [red-first](../red-first/SKILL.md) — committer le test en échec avant le fix.112- [sniper-testing](../sniper-testing/SKILL.md) — tests ciblés pendant l'itération.113- [seam-engineering](../seam-engineering/SKILL.md) — fixer la classe, pas l'instance.114- [repair-loop](../repair-loop/SKILL.md) — le cycle complet fixer-et-atterrir.115116> Crédit d'échafaudage : Matt Pocock, diagnosing-bugs (mattpocock/skills). La composition et les règles dures ici sont BACKS AIOS.