Traçage de la cause racine
Vue d'ensemble
Les bugs se manifestent souvent profondément dans la pile d'appels (git init dans le mauvais répertoire, fichier créé au mauvais endroit, base de données ouverte avec le mauvais chemin). Votre instinct est de corriger où l'erreur apparaît, mais c'est traiter un symptôme.
Principe fondamental : Remontez la chaîne d'appels jusqu'à trouver le déclencheur original, puis corrigez à la source.
Quand l'utiliser
digraph when_to_use {
"Bug appears deep in stack?" [shape=diamond];
"Can trace backwards?" [shape=diamond];
"Fix at symptom point" [shape=box];
"Trace to original trigger" [shape=box];
"Bug appears deep in stack?" -> "Can trace backwards?" [label="yes"];
"Can trace backwards?" -> "Trace to original trigger" [label="yes"];
"Can trace backwards?" -> "Fix at symptom point" [label="no - dead end"];
}
Utilisez quand :
- L'erreur se produit profondément dans l'exécution (pas au point d'entrée)
- La trace de pile affiche une longue chaîne d'appels
- Il n'est pas clair d'où provenaient les données invalides
- Vous devez trouver quel test/code déclenche le problème
Le processus de traçage
1. Observer le symptôme
Error: git init failed in /Users/jesse/project/packages/core
2. Trouver la cause immédiate
Quel code cause directement cela ?
await execFileAsync('git', ['init'], { cwd: projectDir });
3. Demander : Qui a appelé cela ?
WorktreeManager.createSessionWorktree(projectDir, sessionId)
→ called by Session.initializeWorkspace()
→ called by Session.create()
→ called by test at Project.create()
4. Continuer à remonter
Quelle valeur a été passée ?
projectDir = ''(chaîne vide !)- Une chaîne vide comme
cwdse résout enprocess.cwd() - C'est le répertoire du code source !
5. Trouver le déclencheur original
D'où provient la chaîne vide ?
const context = setupCoreTest(); // Returns { tempDir: '' }
Project.create('name', context.tempDir); // Accessed before beforeEach!
Ajouter des traces de pile
Quand vous ne pouvez pas tracer manuellement, ajoutez de l'instrumentation :
// Before the problematic operation
async function gitInit(directory: string) {
const stack = new Error().stack;
console.error('DEBUG git init:', {
directory,
cwd: process.cwd(),
nodeEnv: process.env.NODE_ENV,
stack,
});
await execFileAsync('git', ['init'], { cwd: directory });
}
Important : Utilisez console.error() dans les tests (pas logger - peut ne pas s'afficher)
Exécutez et capturez :
npm test 2>&1 | grep 'DEBUG git init'
Analysez les traces de pile :
- Recherchez les noms de fichiers de test
- Trouvez le numéro de ligne déclenchant l'appel
- Identifiez le motif (même test ? même paramètre ?)
Trouver quel test cause la pollution
Si quelque chose apparaît pendant les tests mais vous ne savez pas quel test :
Utilisez le script de bisection : @find-polluter.sh
./find-polluter.sh '.git' 'src/**/*.test.ts'
Exécute les tests un par un, s'arrête au premier pollueur. Voir le script pour l'utilisation.
Exemple réel : projectDir vide
Symptôme : .git créé dans packages/core/ (code source)
Chaîne de traçage :
git inits'exécute dansprocess.cwd()← paramètre cwd vide- WorktreeManager appelé avec projectDir vide
- Session.create() passée une chaîne vide
- Test accédé à
context.tempDiravant beforeEach - setupCoreTest() renvoie
{ tempDir: '' }initialement
Cause racine : Initialisation de variable de haut niveau accédant à une valeur vide
Correction : Fait de tempDir un getter qui lance une exception s'accédé avant beforeEach
Également ajouté une validation à plusieurs niveaux :
- Couche 1 : Project.create() valide le répertoire
- Couche 2 : WorkspaceManager valide non vide
- Couche 3 : Garde NODE_ENV refuse git init en dehors de tmpdir
- Couche 4 : Journalisation de trace de pile avant git init
Principe clé
digraph principle {
"Found immediate cause" [shape=ellipse];
"Can trace one level up?" [shape=diamond];
"Trace backwards" [shape=box];
"Is this the source?" [shape=diamond];
"Fix at source" [shape=box];
"NEVER fix just the symptom" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];
"Found immediate cause" -> "Can trace one level up?";
"Can trace one level up?" -> "Trace backwards" [label="yes"];
"Can trace one level up?" -> "NEVER fix just the symptom" [label="no"];
"Trace backwards" -> "Is this the source?";
"Is this the source?" -> "Trace backwards" [label="no - keeps going"];
"Is this the source?" -> "Fix at source" [label="yes"];
}
NE corrigez JAMAIS juste où l'erreur apparaît. Remontez pour trouver le déclencheur original.
Conseils pour les traces de pile
Dans les tests : Utilisez console.error() pas logger - logger peut être supprimé
Avant l'opération : Journalisez avant l'opération dangereuse, pas après son échec
Incluez le contexte : Répertoire, cwd, variables d'environnement, timestamps
Capturez la pile : new Error().stack affiche la chaîne d'appels complète
Impact dans le monde réel
D'une session de débogage (2025-10-03) :
- Cause racine trouvée via un traçage de 5 niveaux
- Correction à la source (validation getter)
- 4 couches de défense ajoutées
- 1847 tests réussis, zéro pollution