Denselben Bug zweimal zu beheben ist ein Prozessversagen
Jedes Team hat diesen einen Defekt, der immer wieder zurückkommt. Ein Off-by-one in der Paginierung. Ein fehlender Null-Check im Auth-Middleware. Ein Race Condition im Checkout, den jemand vor drei Sprints “behoben” hat.
Sie haben denselben Bug nicht dreimal geschrieben. Sie haben drei verschiedene Bugs mit derselben Ursache geschrieben. Der Fix behandelte das Symptom. Die Ursache blieb verborgen.
Fagan-Inspections wurden entwickelt, um Defekte zu finden, bevor sie entkommen. Die meisten Teams stoppen in der Nacharbeits-Phase. Der Autor behebt die protokollierten Probleme, der Moderator verifiziert die Fixes, und alle gehen weiter. Das ist ein Fehler. Die Follow-up-Phase ist dort, wo die Kausalanalyse hingehört. Überspringen Sie sie, und Sie planen die nächste Inspection für dieselben Defekttypen.
Was Kausalanalyse tatsächlich bedeutet
Kausalanalyse ist keine Root-Cause-Analyse. Root-Cause-Analyse fragt “welche Codezeile ist fehlgeschlagen und warum.” Kausalanalyse fragt “was an unserem Prozess hat es dieser Defektkategorie erlaubt zu existieren.”
Die Root Cause einer Null-Pointer-Exception ist “wir haben vergessen, auf Null zu prüfen.” Der Kausalanalyse-Befund lautet “unsere Review-Checkliste enthält keine Null-Safety, und unsere Linting-Regeln erlauben ungeprüfte Dereferenzierungen.” Eines behebt den Bug. Das andere repariert die Fabrik.
In einer Fagan-Inspection findet die Kausalanalyse nach der Nacharbeit statt. Der Moderator gruppiert Defekte nach Kategorie und leitet eine kurze Session zur Identifizierung prozessbezogener Ursachen. Der Output sind keine Code-Änderungen. Es sind Prozessänderungen: aktualisierte Checklisten, neue Lint-Regeln, Trainingslücken oder modifizierte Entry-Criteria.
Die Mechanik: So führen Sie sie durch
Beginnen Sie mit dem Inspection-Log. Jeder Defekt sollte bereits vier Felder haben: Ort, Schwere, Typ und Beschreibung. Fügen Sie während der Kausalanalyse ein fünftes hinzu: Prozessursache.
Der Moderator gruppiert Defekte nach Typ. Wenn drei von zwölf Defekten Grenzbedingungsfehler sind, ist das ein Muster. Wenn zwei API-Missbrauchsfehler aus demselben Modul sind, ist das auch ein Muster. Muster sind das Signal. Einzelne Defekte sind Rauschen.
Für jedes Muster stellen Sie drei Fragen:
- Hätten wir diese Kategorie vor der Inspection verhindern können?
- Warum haben unsere bestehenden Präventionsmechanismen sie verpasst?
- Was ist die billigste Änderung, die diese Kategorie beim nächsten Mal verhindern würde?
Die dritte Frage ist, wo die meisten Teams falsch abbiegen. Sie schlagen eine Neuschreibung der Architektur vor. Das ist Wunschdenken. Das Ziel ist die kleinste Prozessänderung, die die Kategorie eliminiert.
Ein Grenzbedingungsmuster könnte bedeuten, “Off-by-one- und Grenzprüfungen” zur Review-Checkliste hinzuzufügen. Ein API-Missbrauchsmuster könnte bedeuten, eine statische Analyse-Regel hinzuzufügen. Ein fehlendes Error-Handling-Muster könnte bedeuten, die Definition of Done zu aktualisieren, um Error-Path-Tests zu verlangen.
Ein funktionierender Kausalanalyse-Tracker
Hier ist ein Python-Skript, das ein Inspection-Log nimmt, Defekte nach Typ gruppiert und nach prozessbezogenen Ursachen fragt.
#!/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()
Speichern Sie ein Inspection-Log als 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"}
]
Führen Sie python causal_analysis.py inspection_log.json aus, und das Skript führt Sie durch die Identifizierung von Prozessursachen. Der erzeugte Bericht wird zur Eingabe für Ihren nächsten Retrospective- oder Prozessverbesserungszyklus.
Warum die meisten Teams das überspringen
Kausalanalyse fügt einem bereits teuren Prozess Zeit hinzu. Eine Standard-Fagan-Inspection für 250 Zeilen kostet 8 bis 12 Personenstunden. Die Kausalanalyse fügt weitere 30 bis 60 Minuten hinzu.
Diese zusätzliche Zeit fühlt sich verschwenderisch an, wenn Sie ein Backlog haben. Aber wenn die Kausalanalyse selbst nur ein Wiederauftreten einer Defektkategorie verhindert, hat sie sich beim nächsten Mal bezahlt gemacht, wenn diese Kategorie nicht auftaucht.
Das schwierigere Problem ist Ehrlichkeit. Kausalanalyse offenbart oft, dass der Defekt existierte, weil das Team einen Schritt übersprungen hat. Der Code wurde vor der Inspection nicht getestet. Der Reviewer hat die Checkliste nicht verwendet. Die Checkliste selbst ist unvollständig.
Diese Befunde können unangenehm sein. Ein Team, das Kausalanalyse als Schuldzuweisung behandelt, wird keine ehrlichen Antworten mehr bekommen. Der Moderator muss es als Prozessverbesserung rahmen, nicht als Accountability-Theater.
Das echte Abwägen: Geschwindigkeit vs. Lernen
Sie haben zwei Optionen nach einer Fagan-Inspection. Schließen Sie den Kreis, indem Sie Fixes verifizieren und weitermachen. Oder schließen Sie den Kreis, indem Sie Fixes verifizieren und lernen, warum die Defekte existierten.
Die erste Option ist heute schneller. Die zweite Option ist in den nächsten sechs Monaten schneller.
Die Teams, die den größten Wert aus Fagan-Inspections ziehen, behandeln das Inspection-Log als Datensatz. Defektmuster sind Feedback. Sie zu ignorieren ist wie eine Test-Suite laufen zu lassen und nie auf die Failures zu schauen.
Nicht jede Defektkategorie verdient eine Kausalanalyse. Ein einmaliger Tippfehler braucht keine Prozessänderung. Aber wenn eine Kategorie in zwei aufeinanderfolgenden Inspections auftaucht, haben Sie ein Prozessproblem, das sich als Code-Problem tarnt.
Beginnen Sie mit dem höchstimpactigen Modul
Sie müssen nicht für jede Inspection eine Kausalanalyse durchführen. Wählen Sie das Modul, das die meisten Production-Incidents oder die meisten entkommenen Defekte produziert. Führen Sie eine vollständige Fagan-Inspection durch, sammeln Sie das Log, und verbringen Sie dreißig Minuten mit Kausalanalyse.
Das erste Mal, wenn Sie das tun, werden die vorgeschlagenen Fixes offensichtlich sein. Aktualisieren Sie die Checkliste. Fügen Sie eine Lint-Regel hinzu. Schreiben Sie eine kurze Team-Notiz über das Muster. Bei der dritten Inspection sollten Sie weniger Defekte in den bereits analysierten Kategorien sehen.
Wenn die Zahlen nicht sinken, sind Ihre vorgeschlagenen Fixes zu vage. “Sei vorsichtiger” ist keine Prozessänderung. “Führe den Null-Safety-Linter in CI aus” ist.
Verfolgen Sie Defektkategorien pro Inspection über die Zeit. Wenn die Kausalanalyse ihre Arbeit tut, verschwinden die wiederkehrenden Kategorien. Wenn nicht, identifizieren Sie entweder die Ursachen falsch oder implementieren die Fixes nicht.
FAQ
Was ist Kausalanalyse in einer Fagan-Inspection?
Kausalanalyse ist ein Post-Inspection-Schritt, bei dem das Team die gefundenen Defekte untersucht, sie nach Kategorien gruppiert und prozessbezogene Ursachen identifiziert. Das Ziel ist die Verhinderung von Wiederholungen durch Änderung von Checklisten, Tools oder Praktiken, nicht nur durch Behebung der einzelnen Defekte.
Wie unterscheidet sich Kausalanalyse von Root-Cause-Analyse?
Root-Cause-Analyse identifiziert den spezifischen Grund für das Auftreten eines einzelnen Defekts, wie z. B. einen fehlenden Null-Check. Kausalanalyse identifiziert, warum der Prozess es erlaubt hat, dass diese gesamte Defektkategorie geschrieben wurde, wie z. B. einen fehlenden Checklistenpunkt oder eine nicht durchgesetzte Lint-Regel.
Wann sollte ich Kausalanalyse durchführen?
Führen Sie sie nach den Nacharbeits- und Follow-up-Phasen einer Fagan-Inspection durch, während die Defekte noch frisch sind. Konzentrieren Sie sich auf Muster, die bei mehreren Defekten auftauchen oder in früheren Inspections aufgetaucht sind. Einmalige Defekte rechtfertigen selten Prozessänderungen.
Wie weiß ich, ob Kausalanalyse funktioniert?
Verfolgen Sie Defektkategorien über Inspections hinweg im Zeitverlauf. Wenn Ihre Kausalanalyse effektiv ist, sollte die Häufigkeit wiederkehrender Kategorien sinken. Wenn dieselben Kategorien weiterhin auftauchen, sind Ihre vorgeschlagenen Fixes entweder falsch oder werden nicht umgesetzt.
Führen Sie es auf Ihrem nächsten Inspection-Log aus
Finden Sie Ihr letztes Inspection-Log. Gruppieren Sie Defekte nach Typ. Für die Top-Kategorie fragen Sie, was die billigste Prozessänderung wäre, um sie beim nächsten Mal zu verhindern.
Schreiben Sie diese Änderung auf. Weisen Sie einen Verantwortlichen zu. Verifizieren Sie sie in der nächsten Inspection. Das ist Kausalanalyse. Alles andere ist nur Bugfixing.