PHP-Anwendungsaudit: Die Methodik hinter einem umfassenden Code-Review
Ein PHP-Anwendungsaudit wird aus einem von drei Gründen beauftragt. Jemand steht kurz davor, das Unternehmen zu kaufen, dem der Code gehört. Jemand hat ein System geerbt, das niemand im aktuellen Team geschrieben hat. Oder jemand steht an der Weggabelung zwischen Refactoring des Bestehenden und Neuentwicklung und will diese Entscheidung nicht aus dem Bauch heraus treffen.
Alle drei brauchen dasselbe: eine objektive Einschätzung des Zustands einer Codebasis, den die Menschen, die ihr am nächsten stehen, nicht mehr klar sehen können. Was sie stattdessen meist bekommen, ist ein Static-Analysis-Bericht mit viertausend Warnungen, der nichts beantwortet. Dieser Beitrag beschreibt, wie der Review tatsächlich strukturiert ist, was jeder Durchgang betrachtet und was am Ende im Bericht stehen muss, damit er sein Geld wert ist.
Was ein umfassendes PHP-Anwendungsaudit abdeckt
Sicherheitsaudits sind eine engere Übung und verdienen eine eigene Behandlung. Ein vollständiges Anwendungsaudit ist breiter angelegt und untersucht sieben Dimensionen, denn eine Codebasis kann sicher und trotzdem unwartbar sein, oder sauber und trotzdem architektonisch festgefahren.
Architektur-Kohärenz. Nicht ob die Architektur modisch ist, sondern ob sie konsistent ist. Die meisten alternden PHP-Anwendungen enthalten zwei oder drei architektonische Epochen übereinander: einen prozeduralen Kern, eine halb fertige MVC-Migration und einen neueren Symfony- oder Laravel-Teil. Diese Schichtung ist normal. Entscheidend ist, ob die Grenzen zwischen den Epochen explizit sind oder ob Geschäftslogik durch alle drei sickert, denn das macht jede Änderung teuer.
Sicherheitsoberfläche. Eingabeverarbeitung, Authentifizierung und Session-Management, Autorisierungsprüfungen auf der richtigen Ebene, Umgang mit Secrets, Datei-Upload-Pfade, SQL-Konstruktion und Deserialisierung. Das Audit kartiert, wo nicht vertrauenswürdige Daten eintreten, und folgt ihnen durch das System.
Gesundheit des Datenmodells. Schema-Normalisierung, Index-Abdeckung gemessen an den tatsächlichen Abfragemustern, Fremdschlüssel-Integrität, Nullable-Spalten, die stillschweigend Bedeutung kodieren, und die Zahl der Stellen, die in dieselbe Tabelle schreiben. Ein Datenmodell mit fünfzehn Schreibern und ohne Durchsetzung von Invarianten ist ein Konsistenzvorfall in Zeitlupe.
Testabdeckung und -qualität. Der Abdeckungsprozentsatz allein ist nahezu bedeutungslos. Entscheidend ist, welche Pfade abgedeckt sind. Eine Anwendung mit 70 Prozent Abdeckung, die Zahlung, Authentifizierung und Berechtigungsprüfungen auslässt, ist weniger sicher als eine mit 35 Prozent, die genau diese abdeckt.
Performance-Hotspots. Abfragen pro Request, N+1-Muster, fehlende Caching-Schichten, synchrone Arbeit, die in eine Queue gehört, und Speicherverhalten unter realistischer Last statt auf einem Entwickler-Laptop.
Abhängigkeits-Hygiene. Aufgegebene Pakete, Versionen mit bekannten Advisories, der Abstand zwischen der installierten PHP-Version und einer unterstützten sowie die Frage, wie viel des Abhängigkeitsbaums transitiv durch ein einziges veraltetes Paket festgenagelt ist.
Deployment-Pipeline. Ob ein Release reproduzierbar ist, ob Rollback eine reale Option ist, wie Migrationen laufen und wie lange es dauert, einen Einzeiler-Fix in Produktion zu bringen. Ein Team, das nicht sicher deployen kann, wird auch nicht sicher refaktorisieren.
Wie der Review strukturiert ist
Die Reihenfolge zählt. Tooling laufen zu lassen, bevor man die Domäne versteht, erzeugt einen Berg von Befunden, an denen keinerlei Priorität hängt.
Phase 1: Discovery-Interview
Zwei bis drei Stunden mit der Person, die das System am besten kennt, plus der Person, die das Budget verantwortet. Die Fragen, die sich lohnen, sind selten technisch:
- Welche Teile der Anwendung vermeidet das Team anzufassen, und was passiert, wenn es doch muss?
- Was war der letzte Vorfall, und was war die tatsächliche Ursache?
- Welche Features stehen für die nächsten zwei Quartale auf der Roadmap?
- Was ist der Umsatzpfad durch dieses System, und welcher Code liegt darauf?
- Wer hat die ältesten Teile geschrieben, und sind diese Personen noch erreichbar?
Die Roadmap-Frage ist die, die das Audit am stärksten verändert. Ein Modul, das hässlich, aber eingefroren ist, hat keine Priorität. Dasselbe Modul unter drei geplanten Features ist das Teuerste in der gesamten Codebasis.
Phase 2: Automatisierte Analyse
Tooling ist der günstige Durchgang, also läuft es früh und breit. Bei einer PHP-Codebasis heißt das: PHPStan oder Psalm auf steigenden Levels, um die Typsicherheits-Basislinie zu kartieren, PHP_CodeSniffer für Abweichungen von Standards, composer audit für bekannte Advisories, PHPMD oder eine Komplexitätsmetrik zur Hotspot-Erkennung und ein Coverage-Lauf, falls eine Testsuite existiert.
Das Ergebnis ist nicht der Befund. Es ist die Landkarte. Statische Analyse sagt Ihnen, wo Sie hinschauen müssen, und die Warnungsdichte pro Verzeichnis ist meist ein besseres Signal als jede einzelne Warnung. Ein Verzeichnis mit der zehnfachen Fehlerdichte seiner Nachbarn ist der Startpunkt des manuellen Durchgangs.
Ein praktischer Hinweis: Auf einer ungeprüften Codebasis erzeugt PHPStan Level 0 allein oft Tausende Fehler. Level für Level zu laufen und die Anzahl pro Level festzuhalten, ergibt ein weit nützlicheres Bild als eine einzelne Zahl. Es zeigt, ob der Code gleichmäßig schwach ist oder ob eine Handvoll Dateien die Schulden trägt.
Phase 3: Manueller Review der kritischen Pfade
Hier fließt die Zeit hin, und hier liegt der Wert. Automatisiertes Tooling kann Ihnen nicht sagen, dass die Rabattberechnung subtil falsch ist, dass die Autorisierung im Controller geprüft wird, aber nicht im Queue-Consumer, der denselben Service anspricht, oder dass zwei Module widersprüchliche Definitionen davon pflegen, was ein aktiver Kunde ist.
Die von Hand geprüften Pfade werden aus dem Discovery-Interview ausgewählt: der Umsatzpfad, der Authentifizierungs- und Autorisierungspfad, alles, was personenbezogene Daten unter der DSGVO verarbeitet, der Lesepfad mit dem höchsten Traffic und das Modul, das die Roadmap als Nächstes anvisiert. Alles andere wird stichprobenartig geprüft statt Zeile für Zeile gelesen.
Phase 4: Bericht und Walkthrough
Der schriftliche Bericht ist das Ergebnis, aber der Walkthrough sorgt dafür, dass er ankommt. Eine Stunde mit dem Team, in der die wichtigsten Befunde im tatsächlichen Code durchgegangen werden, bringt Kontext zutage, der die Schwere in beide Richtungen verändert, und verwandelt den Bericht von einem Urteil in einen Plan.
Was der Bericht enthält
Ein Bericht, der Probleme auflistet, ohne sie zu gewichten, schiebt die schwerste Entscheidung zurück zum Auftraggeber. Drei Komponenten machen ihn handlungsfähig.
Nach Schweregrad geordnete Befunde. Jeder Befund erhält einen Schweregrad, einen Fundort, eine Beschreibung des konkreten Fehlermodus und eine Schätzung des Behebungsaufwands. Der Schweregrad ist eine Funktion von Wahrscheinlichkeit und Wirkungsradius, nicht davon, wie sehr der Prüfer den Code ablehnt. Eine fehlende Autorisierungsprüfung an einem Admin-Endpunkt ist kritisch. Eine God-Klasse mit 2.000 Zeilen, die sich seit drei Jahren nicht geändert hat, ist eine Notiz, kein Notfall.
Behebungsempfehlungen. Konkret, in Reihenfolge gebracht und ehrlich bei den Kosten. "Eine Service-Schicht einführen" ist keine Empfehlung. "Auftragsstatus-Übergänge in einen einzelnen OrderWorkflow-Service extrahieren, die vier unten aufgeführten Aufrufstellen migrieren und Charakterisierungstests im selben Pull Request ergänzen, etwa fünf bis acht Tage" ist eine.
Ein Risikoregister. Die Befunde, die nicht bald behoben werden, müssen trotzdem sichtbar bleiben: was schiefgehen könnte, wie das Frühwarnsignal aussieht und was der Notfallplan ist. Diesen Abschnitt lesen die Nicht-Ingenieure, und er ist oft der Grund, warum das Behebungsbudget genehmigt wird.
Wie die Befunde typischerweise aussehen
Aus einem anonymisierten Engagement bei einer neun Jahre alten Symfony-Anwendung, rund 180.000 Zeilen, sechs Entwickler, im Produktivbetrieb bei einem mittelständischen Logistikkunden. Die wichtigsten Befunde:
- Autorisierung nur in Controllern durchgesetzt. Dieselben Domain-Services waren aus einem Messenger-Consumer und zwei CLI-Kommandos ohne Berechtigungsprüfungen erreichbar. Nicht ausgenutzt, aber nur eine Queue-Nachricht davon entfernt.
- Dreiundzwanzig Schreiber auf der Spalte für den Sendungsstatus, ohne State Machine und mit vier verschiedenen Definitionen eines gültigen Übergangs. Die Grundursache hinter drei der letzten fünf Produktionsvorfälle.
- Ein Lese-Endpunkt mit 340 Abfragen pro Request, weil ein Twig-Template eine Collection innerhalb einer Schleife lazy traversierte. Er war seit zwei Jahren "die langsame Seite", und niemand hatte ihn profiliert.
- PHP 8.1 auf einer Codebasis, die auf 8.3 laufen könnte, zurückgehalten von einer aufgegebenen PDF-Bibliothek, die an genau zwei Stellen verwendet wird.
- Abdeckung bei 61 Prozent, das Abrechnungsmodul bei 4 Prozent.
Keiner dieser Befunde erforderte eine exotische Technik. Sie erforderten jemanden, der den Code liest, ohne die Vorannahme, dass die bestehende Struktur sinnvoll ist. Speziell die Abdeckungslücke in der Abrechnung war intern bekannt und stillschweigend als akzeptabel umklassifiziert worden, denn genau das macht Vertrautheit mit der Zeit mit der Risikowahrnehmung.
Zeitrahmen, und was sie verschiebt
Ein fokussiertes Audit einer kleinen Anwendung, unter 50.000 Zeilen mit einem einzigen Framework und einer laufenden Testsuite, dauert typischerweise drei bis fünf Tage. Eine mittelgroße Anwendung im Bereich von 100.000 bis 250.000 Zeilen mit gemischten Architektur-Epochen läuft zwei bis drei Wochen. Größere oder wirklich undokumentierte Systeme gehen darüber hinaus.
Vier Faktoren verschieben die Zahl stärker als die reine Größe:
- Ob die Anwendung lokal läuft. Wenn ein Prüfer das System nicht am ersten Tag starten kann, wird das Audit zur Archäologie. Das ist die mit Abstand häufigste Ursache für Überschreitungen.
- Architektonische Einheitlichkeit. Ein konsistentes Framework ist weit schneller zu bewerten als drei Epochen halber Migration, unabhängig von der Zeilenzahl.
- Vorhandensein einer Testsuite. Tests sind Dokumentation der Absicht. Ohne sie wird jede Verhaltensfrage zu einer manuellen Nachverfolgung.
- Domänenkomplexität. Versicherungs-, Logistik- und Abrechnungsdomänen tragen Regeln, die sich nicht aus dem Code ableiten lassen, also braucht es mehr Interviewzeit.
Fragen Sie vor Beginn des Engagements nach der Startanleitung. Es ist der günstigste verfügbare Terminschutz.
Aus den Befunden ein Backlog machen
Der Bericht ist ein Input, kein Plan. Die Umwandlung folgt üblicherweise einer einfachen Reihenfolge.
Beheben Sie zuerst die kritischen Befunde zu Sicherheit und Datenintegrität, unabhängig vom Aufwand. Das sind die, bei denen die Kosten des Wartens unbegrenzt sind.
Nehmen Sie als Nächstes die Schnittmenge aus Befundliste und der Roadmap der nächsten zwei Quartale. Strukturarbeit in Code, den Sie ohnehin anfassen werden, zahlt sich sofort aus. Strukturarbeit in Code, den niemand öffnen wird, zahlt sich nie aus.
Setzen Sie dann eine Ratsche statt eines Ziels. Committen Sie eine PHPStan-Baseline, lassen Sie die CI fehlschlagen, wenn sie wächst, und lassen Sie die Schulden schrumpfen, während Leute Dateien anfassen. Das ist haltbarer als ein Aufräum-Sprint, denn es überlebt die erste dringende Unterbrechung.
Der Rest bleibt im Risikoregister mit einem Wiedervorlagedatum. Ein Befund, der ein Jahr unberührt bleibt und keine Probleme verursacht, wurde korrekt herabgestuft, und auch das ist nützliche Information.
Refactoring oder Neuentwicklung
Die Frage, die die meisten Audits auslöst, verdient eine direkte Antwort, und das Audit ist es, was die Antwort verteidigungsfähig statt emotional macht.
Refactoring gewinnt, wenn das Datenmodell im Großen und Ganzen solide ist, die Domänenlogik korrekt ist, auch wenn sie schlecht organisiert ist, und das Team noch liefern kann. Unter diesen Bedingungen ist der Code ein Vermögenswert in schlechter Verpackung, und die über Jahre angesammelte Behandlung von Randfällen ist mehr wert, als sie aussieht.
Eine Neuentwicklung wird vernünftig, wenn das Datenmodell selbst die falschen Domänenkonzepte kodiert, wenn die Plattform wirklich am Ende ihres Lebenszyklus steht und es keinen Migrationspfad gibt, oder wenn niemand mehr da ist, der beschreiben kann, was das System tut. Das sind engere Bedingungen, als die meisten Teams mitten in einem frustrierenden Quartal annehmen. In der Praxis lautet die ehrliche Antwort oft "den Kern refaktorisieren, ein abgegrenztes Modul neu schreiben und die Diskussion über den Rest beenden", und die Befundliste vor sich zu haben, macht dieses Gespräch konkret. Ein Programm zur Legacy-Code-Optimierung, das mit einem Audit beginnt, ist tendenziell realistisch zugeschnitten, weil die Unbekannten bereits eingepreist sind.
Eine zweite Meinung einholen
Ein Audit lohnt sich, wenn eine Entscheidung davon abhängt und die interne Einschätzung umstritten ist. Wenn sich bereits alle einig sind, was falsch ist und was zu tun ist, geben Sie das Geld stattdessen für die Behebung aus.
Bei Wolf-Tech führen wir diesen Prozess auf PHP- und Symfony-Codebasen in ganz Europa durch, meist vor einem Modernisierungsprogramm, einer Finanzierungsrunde oder einer Übernahme. Wenn Sie besprechen möchten, ob ein Audit der richtige nächste Schritt für Ihr System ist, beginnt das bei unserem Code-Quality-Consulting. Schreiben Sie an hello@wolf-tech.io oder schauen Sie auf wolf-tech.io vorbei, und wir sagen Ihnen ehrlich, ob ein Audit das ist, was Sie brauchen.

