Technical Debt in PHP-Projekten priorisieren
AI generated
<?php
8.4
PHP · Legacy · Technical Debt · Priorisierung
Technical Debt in PHP-Projekten priorisieren
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.

18 Min. Lesezeit Hotspot-Analyse · Punktesystem · Refactoring-Backlog PHP 8.x · Legacy-Projekte

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.

11. FAQ: Technical Debt in PHP-Projekten priorisieren

1Technical Debt vs. schlechter Code?
Technical Debt beschreibt bewusste oder unbewusste Kompromisse mit langfristigen Kosten. Für die Priorisierung meist zweitrangig.
2Wie finde ich die größten Hotspots?
Git-Skript zur Commit-Zählung pro Datei, kombiniert mit Komplexitätsmetriken aus PHPMD oder PHPStan.
3Brauche ich ein teures Tool?
Nein, Git-Log, PHPMD und ein eigenes Punktesystem reichen für die meisten Teams völlig aus.
4Wie oft neu bewerten?
Mindestens einmal pro Quartal, da sich Häufigkeit und Risiko über Zeit verschieben.
5Wie viel Zeit für Refactoring reservieren?
Zehn bis zwanzig Prozent der Sprintzeit, fest eingeplant statt als Restposten.
6Wie überzeuge ich das Management?
Konkrete Kosten wie Vorfälle und Hotfix-Stunden pro Hotspot überzeugen mehr als abstrakte Prognosen.
7Muss ich jede Schuld beheben?
Nein, selten genutzte, unkomplexe Dateien mit niedrigem Risiko lohnen den Aufwand oft nicht.
8Was ist WSJF?
Weighted Shortest Job First aus SAFe. Das Punktesystem in diesem Artikel nutzt die Grundidee ohne vollen SAFe-Prozess.
9Wie schätze ich den Aufwand?
Am zuverlässigsten mit historischen Vergleichsdaten, sonst mit einer groben Dreipunktschätzung.
10Verhindert Priorisierung neue Schulden?
Nein, dafür braucht es zusätzlich Code-Reviews, PHPStan in der CI und klare Architekturrichtlinien.