Corriger deux fois le même bug est un échec de processus
Chaque équipe a ce défaut qui ne cesse de revenir. Un off-by-one dans la pagination. Une vérification de null manquante dans le middleware d’authentification. Une race condition dans le checkout que quelqu’un a “corrigée” il y a trois sprints.
Vous n’avez pas écrit le même bug trois fois. Vous avez écrit trois bugs différents avec la même cause. Le correctif a traité le symptôme. La cause est restée cachée.
Les inspections Fagan ont été conçues pour trouver les défauts avant qu’ils ne s’échappent. La plupart des équipes s’arrêtent à la phase de retravail. L’auteur corrige les problèmes enregistrés, le modérateur vérifie les corrections, et tout le monde passe à autre chose. C’est une erreur. La phase de suivi est là où l’analyse causale a sa place. La sauter, c’est programmer la prochaine inspection pour les mêmes types de défauts.
Ce que signifie réellement l’analyse causale
L’analyse causale n’est pas une analyse de cause racine. L’analyse de cause racine demande “quelle ligne de code a échoué et pourquoi.” L’analyse causale demande “qu’est-ce dans notre processus qui a permis à cette catégorie de défaut d’exister.”
Une cause racine d’une exception de pointeur nul est “nous avons oublié de vérifier le null.” Le constat de l’analyse causale est “notre checklist de revue n’inclut pas la null safety, et nos règles de linting autorisent les déréférences non vérifiées.” L’une corrige le bug. L’autre répare l’usine.
Dans une inspection Fagan, l’analyse causale a lieu après le retravail. Le modérateur regroupe les défauts par catégorie et dirige une courte session pour identifier les causes au niveau du processus. Le résultat n’est pas des modifications de code. Ce sont des changements de processus : checklists mises à jour, nouvelles règles de lint, lacunes de formation ou critères d’entrée modifiés.
La mécanique : comment l’exécuter
Commencez avec le log d’inspection. Chaque défaut devrait déjà avoir quatre champs : emplacement, sévérité, type et description. Ajoutez un cinquième pendant l’analyse causale : cause de processus.
Le modérateur regroupe les défauts par type. Si trois des douze défauts sont des erreurs de condition aux limites, c’est un pattern. Si deux sont des erreurs d’utilisation d’API du même module, c’est aussi un pattern. Les patterns sont le signal. Les défauts individuels sont du bruit.
Pour chaque pattern, posez trois questions :
- Aurions-nous pu prévenir cette catégorie avant l’inspection ?
- Pourquoi nos mécanismes de prévention existants l’ont-ils manquée ?
- Quel est le changement le moins cher qui préviendrait cette catégorie la prochaine fois ?
La troisième question est celle où la plupart des équipes se trompent. Elles suggèrent de réécrire l’architecture. C’est un vœu pieux. L’objectif est le plus petit changement de processus qui élimine la catégorie.
Un pattern de condition aux limites pourrait signifier ajouter “vérifications off-by-one et aux limites” à la checklist de revue. Un pattern d’utilisation d’API pourrait signifier ajouter une règle d’analyse statique. Un pattern de gestion d’erreurs manquante pourrait signifier mettre à jour la définition de done pour exiger des tests de chemins d’erreur.
Un tracker d’analyse causale fonctionnel
Voici un script Python qui prend un log d’inspection, regroupe les défauts par type, et demande des causes au niveau du processus.
#!/usr/bin/env python3
"""
Run causal analysis on a Fagan inspection log.
Reads a JSON log, groups defects by type, and emits a causal analysis report.
"""
import json
import argparse
from collections import defaultdict
from dataclasses import dataclass, field
from typing import List, Dict
@dataclass
class Defect:
location: str
severity: str
defect_type: str
description: str
@dataclass
class CausalPattern:
defect_type: str
count: int
locations: List[str] = field(default_factory=list)
process_cause: str = ""
proposed_fix: str = ""
def load_log(path: str) -> List[Defect]:
with open(path, "r") as f:
raw = json.load(f)
return [Defect(**item) for item in raw]
def analyze_patterns(defects: List[Defect]) -> List[CausalPattern]:
groups: Dict[str, List[Defect]] = defaultdict(list)
for d in defects:
groups[d.defect_type].append(d)
patterns = []
for dtype, items in groups.items():
patterns.append(CausalPattern(
defect_type=dtype,
count=len(items),
locations=[d.location for d in items],
))
return sorted(patterns, key=lambda p: p.count, reverse=True)
def prompt_causal_input(patterns: List[CausalPattern]) -> List[CausalPattern]:
print("=== CAUSAL ANALYSIS SESSION ===")
print("For each pattern, identify the process cause and the cheapest fix.\n")
for p in patterns:
print(f"Pattern: {p.defect_type} ({p.count} occurrence(s))")
print(f"Locations: {', '.join(p.locations)}")
p.process_cause = input("Process cause: ").strip()
p.proposed_fix = input("Cheapest prevention fix: ").strip()
print()
return patterns
def emit_report(patterns: List[CausalPattern], output_path: str):
report = {
"summary": {
"total_patterns": len(patterns),
"total_defects": sum(p.count for p in patterns),
},
"patterns": [
{
"type": p.defect_type,
"count": p.count,
"locations": p.locations,
"process_cause": p.process_cause,
"proposed_fix": p.proposed_fix,
}
for p in patterns
],
}
with open(output_path, "w") as f:
json.dump(report, f, indent=2)
print(f"Report written to {output_path}")
def main():
parser = argparse.ArgumentParser(description="Causal analysis for Fagan inspections")
parser.add_argument("log", help="Path to inspection log JSON")
parser.add_argument("--output", default="causal_report.json", help="Output report path")
args = parser.parse_args()
defects = load_log(args.log)
patterns = analyze_patterns(defects)
if not patterns:
print("No defects found. Nothing to analyze.")
return
patterns = prompt_causal_input(patterns)
emit_report(patterns, args.output)
if __name__ == "__main__":
main()
Enregistrez un log d’inspection sous inspection_log.json :
[
{"location": "src/auth.py:42", "severity": "major", "defect_type": "null-safety", "description": "Missing null check on user object"},
{"location": "src/orders.py:88", "severity": "minor", "defect_type": "boundary", "description": "Off-by-one in pagination limit"},
{"location": "src/auth.py:67", "severity": "major", "defect_type": "null-safety", "description": "Unchecked token decode result"}
]
Exécutez python causal_analysis.py inspection_log.json et le script vous guide pour identifier les causes de processus. Le rapport qu’il produit devient l’entrée pour votre prochaine rétrospective ou cycle d’amélioration de processus.
Pourquoi la plupart des équipes sautent cette étape
L’analyse causale ajoute du temps à un processus déjà coûteux. Une inspection Fagan standard pour 250 lignes coûte de 8 à 12 heures-personne. L’analyse causale ajoute encore 30 à 60 minutes.
Ce temps supplémentaire semble du gaspillage quand vous avez un backlog. Mais si l’analyse causale prévient même une seule récurrence d’une catégorie de défaut, elle s’amortit la prochaine fois que cette catégorie n’apparaît pas.
Le problème le plus difficile est l’honnêteté. L’analyse causale révèle souvent que le défaut existait parce que l’équipe a sauté une étape. Le code n’a pas été testé avant l’inspection. Le relecteur n’a pas utilisé la checklist. La checklist elle-même est incomplète.
Ces constats peuvent être inconfortables. Une équipe qui traite l’analyse causale comme une attribution de blame cessera d’obtenir des réponses honnêtes. Le modérateur doit la présenter comme une amélioration de processus, pas comme un théâtre de responsabilisation.
Le vrai compromis : vitesse vs. apprentissage
Vous avez deux options après une inspection Fagan. Clôturer la boucle en vérifiant les corrections et en passant à autre chose. Ou clôturer la boucle en vérifiant les corrections et en apprenant pourquoi les défauts existaient.
La première option est plus rapide aujourd’hui. La deuxième option est plus rapide dans les six prochains mois.
Les équipes qui tirent le plus de valeur des inspections Fagan traitent le log d’inspection comme un jeu de données. Les patterns de défauts sont du feedback. Les ignorer, c’est comme exécuter une suite de tests et ne jamais regarder les échecs.
Toutes les catégories de défauts ne méritent pas une analyse causale. Une faute de frappe isolée ne nécessite pas de changement de processus. Mais si une catégorie apparaît dans deux inspections consécutives, vous avez un problème de processus qui se fait passer pour un problème de code.
Commencez par le module le plus impactant
Vous n’avez pas besoin d’exécuter une analyse causale à chaque inspection. Choisissez le module qui produit le plus d’incidents de production ou le plus de défauts échappés. Exécutez une inspection Fagan complète, collectez le log, et passez trente minutes sur l’analyse causale.
La première fois que vous faites cela, les correctifs proposés seront évidents. Mettez à jour la checklist. Ajoutez une règle de lint. Écrivez une courte note d’équipe sur le pattern. À la troisième inspection, vous devriez voir moins de défauts dans les catégories déjà analysées.
Si les comptes ne baissent pas, vos correctifs proposés sont trop vagues. “Fais plus attention” n’est pas un changement de processus. “Exécute le null safety linter en CI” en est un.
Suivez les catégories de défauts par inspection au fil du temps. Si l’analyse causale fait son travail, les catégories récurrentes disparaissent. Si ce n’est pas le cas, vous identifiez mal les causes ou échouez à mettre en œuvre les correctifs.
FAQ
Qu’est-ce que l’analyse causale dans une inspection Fagan ?
L’analyse causale est une étape post-inspection où l’équipe examine les défauts trouvés, les regroupe par catégorie et identifie les causes au niveau du processus. L’objectif est de prévenir la récurrence en modifiant les checklists, les outils ou les pratiques, pas seulement en corrigeant les défauts individuels.
En quoi l’analyse causale diffère-t-elle de l’analyse de cause racine ?
L’analyse de cause racine identifie la raison spécifique pour laquelle un seul défaut s’est produit, comme une vérification de null manquante. L’analyse causale identifie pourquoi le processus a permis que toute cette catégorie de défaut soit écrite, comme un élément de checklist manquant ou une règle de lint non appliquée.
Quand devrais-je exécuter l’analyse causale ?
Exécutez-la après les phases de retravail et de suivi d’une inspection Fagan, pendant que les défauts sont encore frais. Concentrez-vous sur les patterns qui apparaissent dans plusieurs défauts ou se sont montrés dans des inspections précédentes. Les défauts uniques justifient rarement des changements de processus.
Comment savoir si l’analyse causale fonctionne ?
Suivez les catégories de défauts à travers les inspections au fil du temps. Si votre analyse causale est efficace, la fréquence des catégories récurrentes devrait diminuer. Si les mêmes catégories continuent d’apparaître, vos correctifs proposés sont soit incorrects, soit non mis en œuvre.
Exécutez-la sur votre prochain log d’inspection
Trouvez votre dernier log d’inspection. Regroupez les défauts par type. Pour la catégorie principale, demandez quel serait le changement de processus le moins cher pour la prévenir la prochaine fois.
Écrivez ce changement. Attribuez un responsable. Vérifiez-le dans la prochaine inspection. C’est l’analyse causale. Tout le reste n’est que correction de bugs.