Stratégies de Débogage
Transformez le débogage d'une corvée frustrante en résolution de problèmes systématique grâce à des stratégies éprouvées, des outils puissants et des approches méthodiques.
Quand Utiliser Cette Compétence
- Traquer des bugs insaisissables
- Investiguer les problèmes de performance
- Comprendre des bases de code inconnues
- Déboguer les problèmes de production
- Analyser les dumps mémoire et les stack traces
- Profiler la performance des applications
- Investiguer les fuites mémoire
- Déboguer les systèmes distribués
Principes Fondamentaux
1. La Méthode Scientifique
1. Observer : Quel est le comportement réel ? 2. Hypothétiser : Qu'est-ce qui pourrait le causer ? 3. Expérimenter : Testez votre hypothèse 4. Analyser : A-t-elle prouvé/réfuté votre théorie ? 5. Répéter : Jusqu'à trouver la cause racine
2. Mentalité de Débogage
Ne Pas Supposer :
- « Ça ne peut pas être X » - Si, ça peut l'être
- « Je n'ai pas changé Y » - Vérifiez quand même
- « Ça marche sur ma machine » - Découvrez pourquoi
À Faire :
- Reproduire de façon cohérente
- Isoler le problème
- Tenir des notes détaillées
- Remettre en question tout
- Prendre des pauses quand on est bloqué
3. Rubber Duck Debugging
Expliquez votre code et votre problème à haute voix (à un canard en caoutchouc, un collègue, ou vous-même). Cela révèle souvent le problème.
Processus de Débogage Systématique
Phase 1 : Reproduire
## Checklist de Reproduction
1. **Pouvez-vous la reproduire ?**
- Toujours ? Parfois ? Aléatoirement ?
- Des conditions spécifiques nécessaires ?
- D'autres peuvent-ils la reproduire ?
2. **Créer une reproduction minimale**
- Simplifier à l'exemple le plus petit
- Supprimer le code non apparenté
- Isoler le problème
3. **Documenter les étapes**
- Écrire les étapes exactes
- Noter les détails d'environnement
- Capturer les messages d'erreur
Phase 2 : Recueillir les Informations
## Collecte d'Informations
1. **Messages d'Erreur**
- Stack trace complet
- Codes d'erreur
- Sortie console/logs
2. **Environnement**
- Version du système d'exploitation
- Version du langage/runtime
- Versions des dépendances
- Variables d'environnement
3. **Modifications Récentes**
- Historique Git
- Timeline de déploiement
- Modifications de configuration
4. **Portée**
- Affecte tous les utilisateurs ou des utilisateurs spécifiques ?
- Tous les navigateurs ou des navigateurs spécifiques ?
- Production uniquement ou aussi dev ?
Phase 3 : Former une Hypothèse
## Formation d'une Hypothèse
En fonction des informations recueillies, demandez-vous :
1. **Qu'a changé ?**
- Modifications récentes du code
- Mises à jour de dépendances
- Modifications d'infrastructure
2. **Qu'est-ce qui est différent ?**
- Environnement fonctionnel vs cassé
- Utilisateur fonctionnel vs cassé
- Avant vs après
3. **Où ça pourrait échouer ?**
- Validation des entrées
- Logique métier
- Couche données
- Services externes
Phase 4 : Tester et Vérifier
## Stratégies de Test
1. **Recherche Binaire**
- Commenter la moitié du code
- Affiner la section problématique
- Répéter jusqu'à trouver
2. **Ajouter du Logging**
- console.log/print stratégiques
- Suivre les valeurs des variables
- Tracer le flux d'exécution
3. **Isoler les Composants**
- Tester chaque élément séparément
- Mocker les dépendances
- Supprimer la complexité
4. **Comparer Fonctionnel vs Cassé**
- Diff des configurations
- Diff des environnements
- Diff des données
Outils de Débogage
Débogage JavaScript/TypeScript
// Chrome DevTools Debugger
function processOrder(order: Order) {
debugger; // L'exécution s'arrête ici
const total = calculateTotal(order);
console.log("Total:", total);
// Point d'arrêt conditionnel
if (order.items.length > 10) {
debugger; // S'arrête seulement si la condition est vraie
}
return total;
}
// Techniques de débogage console
console.log("Value:", value); // Basique
console.table(arrayOfObjects); // Format tableau
console.time("operation");
/* code */ console.timeEnd("operation"); // Timing
console.trace(); // Stack trace
console.assert(value > 0, "Value must be positive"); // Assertion
// Profiling de performance
performance.mark("start-operation");
// ... code d'opération
performance.mark("end-operation");
performance.measure("operation", "start-operation", "end-operation");
console.log(performance.getEntriesByType("measure"));
Configuration VS Code Debugger :
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Program",
"program": "${workspaceFolder}/src/index.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"skipFiles": ["<node_internals>/**"]
},
{
"type": "node",
"request": "launch",
"name": "Debug Tests",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": ["--runInBand", "--no-cache"],
"console": "integratedTerminal"
}
]
}
Débogage Python
# Debugger intégré (pdb)
import pdb
def calculate_total(items):
total = 0
pdb.set_trace() # Le debugger démarre ici
for item in items:
total += item.price * item.quantity
return total
# Breakpoint (Python 3.7+)
def process_order(order):
breakpoint() # Plus pratique que pdb.set_trace()
# ... code
# Débogage post-mortem
try:
risky_operation()
except Exception:
import pdb
pdb.post_mortem() # Déboguer au point d'exception
# IPython debugging (ipdb)
from ipdb import set_trace
set_trace() # Meilleure interface que pdb
# Logging pour le débogage
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def fetch_user(user_id):
logger.debug(f'Fetching user: {user_id}')
user = db.query(User).get(user_id)
logger.debug(f'Found user: {user}')
return user
# Profiler la performance
import cProfile
import pstats
cProfile.run('slow_function()', 'profile_stats')
stats = pstats.Stats('profile_stats')
stats.sort_stats('cumulative')
stats.print_stats(10) # Top 10 des plus lentes
Débogage Go
// Delve debugger
// Installation : go install github.com/go-delve/delve/cmd/dlv@latest
// Exécuter : dlv debug main.go
import (
"fmt"
"runtime"
"runtime/debug"
)
// Afficher la stack trace
func debugStack() {
debug.PrintStack()
}
// Récupération de panic avec débogage
func processRequest() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Panic:", r)
debug.PrintStack()
}
}()
// ... code qui pourrait paniquer
}
// Profiling mémoire
import _ "net/http/pprof"
// Visitez http://localhost:6060/debug/pprof/
// Profiling CPU
import (
"os"
"runtime/pprof"
)
f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// ... code à profiler
Techniques de Débogage Avancées
Technique 1 : Débogage par Recherche Binaire
# Git bisect pour trouver une régression
git bisect start
git bisect bad # Le commit actuel est mauvais
git bisect good v1.0.0 # v1.0.0 était bon
# Git vérifie le commit du milieu
# Testez-le, puis :
git bisect good # si ça marche
git bisect bad # si c'est cassé
# Continuez jusqu'à trouver le bug
git bisect reset # quand vous avez terminé
Technique 2 : Débogage Différentiel
Comparez le fonctionnel et le cassé :
## Qu'est-ce qui est Différent ?
| Aspect | Fonctionnel | Cassé |
| ------------ | ----------- | -------------- |
| Environnement| Développement | Production |
| Version Node | 18.16.0 | 18.15.0 |
| Données | BD vide | 1M d'enregistrements |
| Utilisateur | Admin | Utilisateur régulier |
| Navigateur | Chrome | Safari |
| Heure | Pendant le jour | Après minuit |
Hypothèse : Problème basé sur l'heure ? Vérifiez la gestion des fuseaux horaires.
Technique 3 : Débogage par Traçage
// Traçage des appels de fonction
function trace(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`${propertyKey} returned:`, result);
return result;
};
return descriptor;
}
class OrderService {
@trace
calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
}
Technique 4 : Détection de Fuites Mémoire
// Chrome DevTools Memory Profiler
// 1. Prendre un snapshot de heap
// 2. Effectuer une action
// 3. Prendre un autre snapshot
// 4. Comparer les snapshots
// Débogage mémoire Node.js
if (process.memoryUsage().heapUsed > 500 * 1024 * 1024) {
console.warn("High memory usage:", process.memoryUsage());
// Générer un dump de heap
require("v8").writeHeapSnapshot();
}
// Trouver les fuites mémoire dans les tests
let beforeMemory: number;
beforeEach(() => {
beforeMemory = process.memoryUsage().heapUsed;
});
afterEach(() => {
const afterMemory = process.memoryUsage().heapUsed;
const diff = afterMemory - beforeMemory;
if (diff > 10 * 1024 * 1024) {
// Seuil de 10MB
console.warn(`Possible memory leak: ${diff / 1024 / 1024}MB`);
}
});
Patrons de Débogage par Type de Problème
Patron 1 : Bugs Intermittents
## Stratégies pour les Bugs Flaky
1. **Ajouter un logging extensif**
- Logger les informations de timing
- Logger toutes les transitions d'état
- Logger les interactions externes
2. **Chercher les race conditions**
- Accès concurrent à l'état partagé
- Opérations asynchrones s'achevant dans le désordre
- Synchronisation manquante
3. **Vérifier les dépendances de timing**
- setTimeout/setInterval
- Ordre de résolution des Promises
- Timing des animation frames
4. **Test de charge**
- Exécuter plusieurs fois
- Varier le timing
- Simuler la charge
Patron 2 : Problèmes de Performance
## Débogage de Performance
1. **Profiler d'abord**
- Ne pas optimiser à l'aveugle
- Mesurer avant et après
- Trouver les goulots
2. **Coupables courants**
- Requêtes N+1
- Re-rendus inutiles
- Grand traitement de données
- E/S synchrone
3. **Outils**
- Chrome DevTools Performance tab
- Lighthouse
- Python : cProfile, line_profiler
- Node : clinic.js, 0x
Patron 3 : Bugs de Production
## Débogage de Production
1. **Recueillir des preuves**
- Error tracking (Sentry, Bugsnag)
- Logs d'application
- Signalements utilisateur
- Métriques/monitoring
2. **Reproduire localement**
- Utiliser les données de production (anonymisées)
- Correspondre à l'environnement
- Suivre les étapes exactes
3. **Investigation sécurisée**
- Ne pas changer la production
- Utiliser les feature flags
- Ajouter du monitoring/logging
- Tester les correctifs en staging
Bonnes Pratiques
- Reproduire d'Abord : On ne peut pas corriger ce qu'on ne peut pas reproduire
- Isoler le Problème : Supprimer la complexité jusqu'au cas minimal
- Lire les Messages d'Erreur : Ils sont généralement utiles
- Vérifier les Modifications Récentes : La plupart des bugs sont récents
- Utiliser le Contrôle de Version : Git bisect, blame, historique
- Prendre des Pauses : Un regard neuf voit mieux
- Documenter les Résultats : Aider votre vous du futur
- Corriger la Cause Racine : Pas seulement les symptômes
Erreurs Courantes en Débogage
- Faire Plusieurs Modifications : Changer une seule chose à la fois
- Ne Pas Lire les Messages d'Erreur : Lire la stack trace complète
- Supposer que C'est Complexe : Souvent c'est simple
- Logging de Débogage en Prod : Supprimer avant de livrer
- Ne Pas Utiliser le Debugger : console.log n'est pas toujours le mieux
- Abandonner Trop Tôt : La persévérance paie
- Ne Pas Tester le Correctif : Vérifier qu'il fonctionne réellement
Checklist Rapide de Débogage
## Quand Vous Êtes Bloqué, Vérifier :
- [ ] Erreurs d'orthographe (typos dans les noms de variables)
- [ ] Sensibilité à la casse (fileName vs filename)
- [ ] Valeurs null/undefined
- [ ] Décalage d'index de tableau
- [ ] Timing asynchrone (race conditions)
- [ ] Problèmes de portée (closure, hoisting)
- [ ] Incohérences de type
- [ ] Dépendances manquantes
- [ ] Variables d'environnement
- [ ] Chemins de fichier (absolu vs relatif)
- [ ] Problèmes de cache (vider le cache)
- [ ] Données obsolètes (rafraîchir la base de données)