Eine nachvollziehbare Methode statt Bauchgefühl
Jedes gewachsene PHP-Projekt sammelt technische Schulden an, die Frage ist nie ob, sondern welche zuerst behoben werden. Dieser Artikel zeigt eine praktische Methode, technical debt anhand von Änderungshäufigkeit, Fehlerrisiko und Behebungsaufwand zu bewerten und daraus eine belastbare Reihenfolge abzuleiten.
Inhaltsverzeichnis
- 1. Warum eine Liste bekannter Probleme nicht reicht
- 2. Drei Arten von Technical Debt unterscheiden
- 3. Hotspot-Analyse: Änderungshäufigkeit aus Git ermitteln
- 4. Komplexität mit statischen Metriken messen
- 5. Ein Punktesystem aus Risiko, Häufigkeit und Aufwand
- 6. Den Refactoring-Backlog aufbauen und pflegen
- 7. Ein festes Zeitbudget statt Grundsatzdiskussionen
- 8. Technical Debt gegenüber dem Management kommunizieren
- 9. Priorisierungsmethoden im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum eine Liste bekannter Probleme nicht reicht
Technical Debt in PHP-Projekten wird selten dokumentiert, meist existiert es nur als geteiltes Wissen im Team, welche Klasse man besser nicht anfasst und welche Datei bei jeder Änderung Angst auslöst. Das Problem an diesem impliziten Wissen: Es ist nicht priorisiert, nicht messbar und verschwindet mit jedem Entwickler, der das Projekt verlässt. Eine bloße Liste bekannter Probleme, wie sie viele Teams als Wiki-Seite führen, löst dieses Problem nur teilweise, weil sie keine Reihenfolge vorgibt und schnell veraltet.
Der eigentliche Engpass ist nicht das Erkennen von Technical Debt, jeder erfahrene Entwickler kann in einer Stunde ein Dutzend Problemstellen benennen. Der Engpass ist die Priorisierung: Welche der zwanzig bekannten Probleme sollten in diesem Quartal behoben werden, wenn die Zeit nur für drei reicht? Ohne eine nachvollziehbare Methode wird diese Entscheidung entweder vom lautesten Entwickler im Raum getroffen oder von demjenigen, dessen Ärger über ein bestimmtes Modul gerade am frischesten ist.
Eine belastbare Priorisierung braucht drei Zutaten, die in den folgenden Abschnitten Schritt für Schritt aufgebaut werden: eine objektive Methode, um zu messen, wie oft ein Codebereich tatsächlich angefasst wird, eine Einschätzung des Risikos, das von einem Problem ausgeht, und eine realistische Schätzung des Behebungsaufwands. Erst die Kombination dieser drei Faktoren macht aus einer Liste subjektiver Meinungen eine nachvollziehbare Reihenfolge für Technical Debt.
2. Drei Arten von Technical Debt unterscheiden
Nicht jede Form von Technical Debt verdient dieselbe Aufmerksamkeit. Es lohnt sich, mindestens drei Kategorien zu unterscheiden: bewusst eingegangene Schulden, etwa ein pragmatischer Workaround unter Zeitdruck, der von Anfang an als temporär markiert wurde; unbewusst entstandene Schulden, die erst durch neue Erkenntnisse oder wachsende Anforderungen als Problem sichtbar werden; und veraltete Schulden, die durch Weiterentwicklung von PHP selbst oder eingesetzten Bibliotheken entstehen, etwa der Einsatz einer inzwischen als unsicher geltenden Funktion.
Diese Unterscheidung ist keine akademische Übung, sie beeinflusst direkt die Priorisierung. Bewusst eingegangene Schulden mit klarer Dokumentation sind oft leichter zu beheben, weil ihr Kontext bekannt ist. Unbewusste Schulden erfordern zunächst eine Analyse, um das tatsächliche Ausmaß zu verstehen. Veraltete Schulden, etwa durch ein PHP-Versions-Upgrade ausgelöst, haben oft eine harte Deadline, weil eine alte Funktion in der nächsten Major-Version entfernt wird, was sie automatisch nach oben priorisiert, unabhängig von anderen Kriterien.
3. Hotspot-Analyse: Änderungshäufigkeit aus Git ermitteln
Der wichtigste objektive Faktor bei der Priorisierung von Technical Debt ist nicht, wie schlecht eine Datei aussieht, sondern wie oft sie tatsächlich geändert wird. Eine hässliche, aber seit Jahren unveränderte Datei kostet das Team wenig, weil niemand sie anfasst. Eine mittelmäßige Datei, die jede Woche mehrfach geändert wird, kostet dagegen kontinuierlich Zeit und Nerven. Diese Erkenntnis, oft als Hotspot-Analyse bezeichnet, lässt sich direkt aus der Git-Historie ableiten, ganz ohne subjektive Einschätzung.
Das folgende Skript zählt für jede PHP-Datei im Projekt, wie viele Commits sie in den letzten zwölf Monaten berührt haben, und liefert damit eine erste, objektive Rangliste der am häufigsten geänderten Dateien, unabhängig von deren wahrgenommener Codequalität.
#!/usr/bin/env bash
# hotspot-analysis.sh — rank PHP files by change frequency (last 12 months)
set -euo pipefail
git log --since="12 months ago" --name-only --pretty=format: -- '*.php' \
| grep -v '^$' \
| sort \
| uniq -c \
| sort -rn \
| head -30 \
| awk '{printf "%-6s %s\n", $1, $2}'
# Output example:
# 142 app/code/Legacy/Checkout/Model/OrderProcessor.php
# 98 app/code/Legacy/Catalog/Model/PriceCalculator.php
# 67 app/code/Legacy/Customer/Model/AccountManager.php
Kombiniert man diese Änderungshäufigkeit mit einer Komplexitätsmetrik der jeweiligen Datei, entsteht eine deutlich aussagekräftigere Priorisierung als reines Bauchgefühl: Eine Datei, die sowohl häufig geändert wird als auch hochkomplex ist, verursacht mit hoher Wahrscheinlichkeit den größten laufenden Schaden und sollte entsprechend priorisiert werden.
4. Komplexität mit statischen Metriken messen
Änderungshäufigkeit allein sagt noch nichts über das tatsächliche Risiko einer Datei aus. Eine einfache, gut strukturierte Datei, die häufig geändert wird, ist unproblematisch, solange Änderungen leicht und sicher vorgenommen werden können. Erst die Kombination aus hoher Änderungshäufigkeit und hoher zyklomatischer Komplexität macht eine Datei zu einem echten Risiko für Technical Debt-Priorisierung.
Tools wie PHPMD (PHP Mess Detector) oder PHPStan mit aktivierten Komplexitätsregeln liefern pro Methode eine zyklomatische Komplexität, also die Anzahl unabhängiger Pfade durch den Code. Eine Methode mit einer Komplexität über fünfzehn gilt in den meisten Teams als klarer Kandidat für Refactoring, unabhängig davon, wie oft sie geändert wird. Kombiniert mit der Änderungshäufigkeit aus Abschnitt drei ergibt sich eine Matrix, in der sich vier Quadranten unterscheiden lassen: hohe Komplexität mit hoher Änderungshäufigkeit als klarer Sofort-Kandidat, hohe Komplexität mit niedriger Änderungshäufigkeit als beobachtbar, aber nicht dringend, und die beiden entsprechend gespiegelten, weniger kritischen Fälle.
#!/usr/bin/env bash
# complexity-report.sh — run PHPMD cyclomatic complexity rule on hotspots
set -euo pipefail
vendor/bin/phpmd app/code/Legacy/Checkout/Model/OrderProcessor.php \
text cyclomatic-complexity
# Example output:
# OrderProcessor.php:42 The method calculateTotal() has a Cyclomatic
# Complexity of 28. The configured cyclomatic complexity threshold is 10.
# OrderProcessor.php:118 The method applyDiscounts() has a Cyclomatic
# Complexity of 19. The configured cyclomatic complexity threshold is 10.
5. Ein Punktesystem aus Risiko, Häufigkeit und Aufwand
Die reine Kombination aus Änderungshäufigkeit und Komplexität reicht für eine belastbare Priorisierung noch nicht aus, weil sie den Behebungsaufwand außer Acht lässt. Ein Problem mit hohem Risiko, das sich aber nur mit zwei Wochen Aufwand beheben lässt, konkurriert real mit einem Problem mit mittlerem Risiko, das in einem Tag gelöst werden kann. Ein einfaches Punktesystem löst diesen Zielkonflikt, indem es Risiko und Aufwand explizit gegeneinander aufwiegt.
Das folgende PHP-Skript berechnet für eine Liste erfasster technischer Schulden einen Priorisierungswert nach der Formel Risiko mal Häufigkeit geteilt durch Aufwand, angelehnt an das aus dem Projektmanagement bekannte WSJF-Prinzip (Weighted Shortest Job First). Je höher der Wert, desto günstiger das Verhältnis aus Nutzen und Kosten der Behebung.
<?php
declare(strict_types=1);
// Weighted Shortest Job First style prioritization for technical debt items
final class TechnicalDebtItem
{
public function __construct(
public readonly string $description,
public readonly int $risk, // 1 (low) to 5 (critical)
public readonly int $frequency, // changes per quarter, from git log
public readonly int $effort, // estimated days to fix
) {
}
public function priorityScore(): float
{
// Higher score = better ratio of benefit to cost
return ($this->risk * $this->frequency) / max(1, $this->effort);
}
}
$items = [
new TechnicalDebtItem('OrderProcessor: no test coverage', risk: 5, frequency: 12, effort: 8),
new TechnicalDebtItem('PriceCalculator: cyclomatic complexity 28', risk: 4, frequency: 9, effort: 3),
new TechnicalDebtItem('AccountManager: deprecated mysql_* calls', risk: 3, frequency: 4, effort: 2),
];
usort($items, static fn (TechnicalDebtItem $a, TechnicalDebtItem $b): int =>
$b->priorityScore() <=> $a->priorityScore());
foreach ($items as $item) {
printf("%.2f %s\n", $item->priorityScore(), $item->description);
}
Der Wert des Skripts liegt nicht in mathematischer Präzision, Risiko und Aufwand bleiben Schätzungen. Der eigentliche Gewinn ist, dass alle Beteiligten dieselben drei Zahlen diskutieren müssen, statt eine Rangfolge nach Gefühl zu verhandeln. Ein Team, das über die Risikoeinschätzung von OrderProcessor streitet, streitet über eine konkrete, überprüfbare Zahl statt über ein diffuses Unbehagen.
6. Den Refactoring-Backlog aufbauen und pflegen
Sobald ein Punktesystem etabliert ist, braucht Technical Debt einen festen Platz im Projektmanagement, statt nur in Commit-Kommentaren mit "TODO: refactor" erwähnt zu werden. Ein eigenes Backlog, parallel zum Feature-Backlog gepflegt, macht Schulden sichtbar und diskutierbar, statt sie in Code-Kommentaren verschwinden zu lassen, wo sie niemand mehr systematisch sieht.
Wichtig ist, dieses Backlog regelmäßig neu zu bewerten, mindestens einmal pro Quartal, weil sich sowohl Änderungshäufigkeit als auch Risiko im Laufe der Zeit verschieben. Eine Datei, die vor einem Jahr ein Hotspot war, kann nach einem erfolgreichen Refactoring aus der Liste verschwinden, während eine neue Datei durch gestiegene Nutzung plötzlich zum neuen Hotspot wird. Ein statisches, einmal erstelltes Backlog verliert schnell an Aussagekraft, wenn es nicht mit aktuellen Git-Daten synchron gehalten wird.
# technical-debt-backlog.yaml — reviewed quarterly, kept next to the code
items:
- description: "OrderProcessor: no test coverage"
risk: 5
frequency: 12
effort_days: 8
priority_score: 7.5
status: "in_progress"
owner: "checkout-team"
- description: "PriceCalculator: cyclomatic complexity 28"
risk: 4
frequency: 9
effort_days: 3
priority_score: 12.0
status: "backlog"
owner: "catalog-team"
- description: "AccountManager: deprecated mysql_* calls"
risk: 3
frequency: 4
effort_days: 2
priority_score: 6.0
status: "backlog"
owner: "customer-team"
7. Ein festes Zeitbudget statt Grundsatzdiskussionen
Eine der wirksamsten organisatorischen Maßnahmen gegen wachsende Technical Debt ist ein fester Prozentsatz der Entwicklungszeit, der grundsätzlich für Refactoring reserviert ist, üblich sind zwischen zehn und zwanzig Prozent pro Sprint. Dieser Ansatz vermeidet die immer wiederkehrende Grundsatzdiskussion, ob in diesem Sprint überhaupt Zeit für Refactoring da ist, und ersetzt sie durch eine feste Regel, die nur noch die Auswahl der konkreten Maßnahme offenlässt.
Entscheidend ist dabei, dieses Budget tatsächlich für die höchstpriorisierten Einträge aus dem Backlog zu nutzen, statt für spontane, unpriorisierte Aufräumarbeiten. Ein Team, das sein Refactoring-Budget für die zufällig gerade störende Datei statt für den höchstbewerteten Hotspot einsetzt, verschenkt einen Großteil des strukturellen Nutzens der Priorisierungsarbeit aus den vorherigen Abschnitten.
8. Technical Debt gegenüber dem Management kommunizieren
Das Punktesystem aus Abschnitt fünf hat einen wichtigen Nebeneffekt: Es übersetzt eine technische Diskussion in eine Sprache, die auch ohne PHP-Kenntnisse verständlich ist. Statt "Der Code in diesem Modul ist schlecht" lässt sich sagen "Dieses Modul wird zwölfmal pro Quartal geändert, hat ein hohes Fehlerrisiko, und die Behebung kostet drei Tage, mit einem der besten Kosten-Nutzen-Verhältnisse im aktuellen Backlog". Diese Übersetzung ist entscheidend, um Budget für Refactoring gegenüber Stakeholdern zu rechtfertigen, die primär an Feature-Fortschritt gemessen werden.
Ebenso wichtig ist, konkrete, bereits eingetretene Kosten zu dokumentieren: Wie viele Produktionsvorfälle gingen in den letzten sechs Monaten auf einen bestimmten Hotspot zurück, wie viele Stunden wurden für Hotfixes in diesem Modul aufgewendet. Diese Zahlen aus der Vergangenheit sind oft überzeugender als jede Prognose über zukünftiges Risiko, weil sie bereits eingetretenen, unbestreitbaren Schaden belegen.
<?php
declare(strict_types=1);
// Turns raw incident data into a stakeholder-readable summary line
final class DebtImpactReport
{
public function __construct(
private readonly string $module,
private readonly int $incidentsLastSixMonths,
private readonly float $hotfixHoursSpent,
) {
}
public function summaryLine(): string
{
return sprintf(
'%s: %d production incidents, %.1f hotfix hours in six months',
$this->module,
$this->incidentsLastSixMonths,
$this->hotfixHoursSpent
);
}
}
$report = new DebtImpactReport(
module: 'OrderProcessor',
incidentsLastSixMonths: 7,
hotfixHoursSpent: 34.5,
);
echo $report->summaryLine();
9. Priorisierungsmethoden im Vergleich
Es gibt mehrere etablierte Ansätze, um Technical Debt zu priorisieren. Die folgende Tabelle vergleicht die wichtigsten Methoden nach Aufwand für die Einführung und Aussagekraft der Ergebnisse.
| Methode | Einführungsaufwand | Aussagekraft | Eignung |
|---|---|---|---|
| Bauchgefühl im Team | Keiner | Niedrig, subjektiv und inkonsistent | Nur für sehr kleine Projekte tolerierbar |
| Hotspot-Analyse allein | Gering, ein Git-Skript | Mittel, ignoriert Behebungsaufwand | Guter erster Schritt für jedes Team |
| Punktesystem (Risiko/Häufigkeit/Aufwand) | Mittel, erfordert Schätzungen | Hoch, nachvollziehbar und diskutierbar | Empfohlen für mittlere bis große Teams |
| Formales WSJF (SAFe) | Hoch, eigener Prozess nötig | Sehr hoch, aber oft Overkill | Nur bei bereits etablierten SAFe-Prozessen |
Für die meisten PHP-Projekte ist das Punktesystem aus Abschnitt fünf der beste Kompromiss: Es verlangt keinen kompletten Prozessumbau wie formales WSJF, liefert aber deutlich belastbarere Ergebnisse als reine Hotspot-Analyse oder Bauchgefühl. Der Einstieg gelingt am einfachsten über die Kombination aus Git-Hotspot-Skript und der einfachen Punkteformel aus dem vorherigen Abschnitt.
Mironsoft
PHP-Legacy-Modernisierung und Magento-Entwicklung
Technical Debt endlich nachvollziehbar priorisieren?
Wir führen eine Hotspot-Analyse eurer Codebasis durch, bewerten Risiko und Aufwand systematisch und liefern einen priorisierten Refactoring-Backlog, den ihr direkt gegenüber dem Management vertreten könnt.
Hotspot-Analyse
Änderungshäufigkeit und Komplexität aus Git und PHPStan ableiten
Priorisierung
Punktesystem aus Risiko, Häufigkeit und Aufwand aufbauen
Kommunikation
Belastbare Kennzahlen für Stakeholder-Gespräche aufbereiten
10. Zusammenfassung
Technical Debt in PHP-Projekten zu priorisieren gelingt nicht durch eine reine Liste bekannter Probleme, sondern durch eine Kombination aus objektiven Daten: Änderungshäufigkeit aus der Git-Historie, Komplexität aus statischen Analysewerkzeugen wie PHPMD oder PHPStan, und eine realistische Schätzung des Behebungsaufwands. Ein einfaches Punktesystem nach dem Prinzip Risiko mal Häufigkeit geteilt durch Aufwand macht aus subjektiven Diskussionen eine nachvollziehbare, diskutierbare Rangfolge.
Ein eigenes Refactoring-Backlog, ein festes Zeitbudget pro Sprint und eine verständliche Kommunikation gegenüber dem Management sorgen dafür, dass die Priorisierung nicht bei der Theorie bleibt, sondern tatsächlich in kontinuierliche Verbesserung übersetzt wird. Der größte Fehler bei technical debt ist nicht, sie zu haben, jedes wachsende Projekt hat sie, sondern sie unpriorisiert und unsichtbar wachsen zu lassen, bis ein einzelner Hotspot zum echten Betriebsrisiko wird.
Technical Debt priorisieren — Das Wichtigste auf einen Blick
Hotspot-Analyse
Änderungshäufigkeit aus Git zeigt objektiv, welche Dateien tatsächlich laufend Kosten verursachen.
Punktesystem
Risiko mal Häufigkeit geteilt durch Aufwand liefert eine nachvollziehbare, diskutierbare Priorität.
Festes Budget
Zehn bis zwanzig Prozent der Sprintzeit fest für die höchstpriorisierten Backlog-Einträge reservieren.
Kommunikation
Konkrete Kennzahlen zu Vorfällen und Hotfix-Stunden überzeugen Stakeholder mehr als Prognosen.