Regression ist ein Fehler, der nach Änderungen am Code auftritt, obwohl dieselbe Funktionalität zuvor korrekt funktioniert hat. Regression bedeutet, dass eine neue Änderung etwas zerstört hat, was bereits geschrieben und getestet wurde. Es ist eines der häufigsten und gefährlichsten Probleme in der Entwicklung: Bei der Behebung eines Fehlers kann ein Entwickler unbeabsichtigt drei andere Funktionen beschädigen. Laut Capers Jones Software Engineering 2023 beträgt die durchschnittliche Dichte von Regressionen 1–3 pro 100 geänderte Codezeilen. Wir untersuchen die Ursachen von Regressionen, Methoden zu ihrer Erkennung und Präventionsstrategien.
Wichtige Punkte
Regression ist eine Situation, in der eine Funktionalität, die in einer früheren Version funktioniert hat, nach Änderungen nicht mehr funktioniert. Die Änderung kann alles sein: Fehlerbehebung, Hinzufügen einer neuen Funktion, Refactoring, Bibliotheksaktualisierung oder sogar eine Konfigurationsänderung. Regression ist der Hauptfeind der Stabilität: Jede Änderung riskiert, etwas zu beschädigen, das bereits verifiziert und veröffentlicht wurde.
Der Begriff kommt aus dem Testen: Regressionstest ist die erneute Ausführung vorhandener Tests nach jeder Änderung. Wenn ein zuvor bestandener Test fehlschlägt, ist eine Regression aufgetreten. Im weiteren Sinne ist Regression nicht nur ein Testfehler, sondern jede vom Benutzer oder QA bemerkte Verschlechterung des Verhaltens. Laut Tricentis State of Testing 2023 machen Regressionen 35–45% aller in der Produktion gefundenen Fehler aus.
Was eine Regression von einem gewöhnlichen Fehler unterscheidet, ist der zeitliche Kontext: Ein Fehler könnte schon immer existiert haben, während eine Regression immer das Ergebnis einer Änderung ist. Diese Unterscheidung ist wichtig, denn die Suche nach der Ursache einer Regression beginnt mit der Analyse, was sich zwischen “funktionierte” und “funktioniert nicht mehr” verändert hat. Git bisect ist das Standardwerkzeug, um den Commit zu finden, der die Regression verursacht hat.
Lokale Regression — eine Änderung in Modul A beschädigt die Funktionalität im selben Modul A. Beispiel: Ein Entwickler schreibt eine Sortierfunktion um und sie hört auf, ein leeres Array korrekt zu behandeln. Die lokale Regression ist am einfachsten zu erkennen und zu beheben, da Ursache und Wirkung nahe beieinander liegen.
Entfernte Regression — eine Änderung in Modul A beschädigt die Funktionalität in Modul B, das nicht direkt durch Code, sondern durch Daten oder Zeitsteuerung verbunden ist. Beispiel: Eine Änderung des Datenbankschemas im Modul „Benutzer“ zerstört einen Bericht im Modul „Analytik“, der dieselbe Tabelle verwendet. Entfernte Regressionen sind die heimtückischsten: Der Entwickler ahnt nicht, dass seine Änderung ein anderes Modul betrifft.
Nebenwirkungs-Regression — eine Änderung an einem Nebeneffekt (Logging, Caching, Senden von Benachrichtigungen) zerstört das erwartete Verhalten. Beispiel: Ein Entwickler fügt Caching hinzu, um die Leistung zu verbessern, aber aufgrund veralteter Caches sehen Benutzer alte Daten. Nebenwirkungs-Regressionen sind mit automatischen Tests schwer zu fangen, da Nebeneffekte oft nicht von Tests abgedeckt werden.
Performance-Regression — der Code funktioniert weiterhin funktional korrekt, ist aber langsamer als zuvor. Beispiel: Ein neuer Verschlüsselungsalgorithmus liefert die gleichen Ergebnisse, aber die Ausführungszeit stieg von 2 ms auf 200 ms. Performance-Regressionen werden von gewöhnlichen Unit-Tests nicht erkannt — Benchmarks und Profiling sind erforderlich.
| Regressionsart | Beispiel | Erkennungsmethode |
|---|---|---|
| Lokal | Defekte Sortierung | Unit-Tests |
| Entfernt | DB-Schemaänderung | Integrationstests |
| Nebenwirkung | Veralteter Cache | E2E-Tests |
| Performance | Langsame Antwort | Benchmarks |
Die erste Ursache ist Code-Kopplung. Je stärker Module voneinander abhängen, desto wahrscheinlicher ist es, dass eine Änderung in einem eine Regression in einem anderen verursacht. Klassische Antipatterns: God Object (ein Objekt, das alles macht), Shotgun Surgery (eine Änderung an einer Stelle erfordert Bearbeitungen an Dutzenden Stellen), zirkuläre Abhängigkeit. Die Kopplung zu reduzieren ist eine Frage der Architektur: SOLID-Prinzipien, Dependency Injection, hexagonale Architektur.
Die zweite Ursache ist fehlende Tests für die geänderte Funktionalität. Wenn Code nicht durch Tests abgedeckt ist, erfährt der Entwickler erst von QA oder Benutzern von einer Regression. Laut Google Testing Blog haben Projekte mit >75% Testabdeckung 5-mal weniger Regressionen als Projekte mit <25% Abdeckung. TDD (Test-Driven Development) stellt sicher, dass Tests vor dem Code geschrieben werden, nicht „wenn Zeit ist“.
Die dritte Ursache ist der menschliche Faktor. Der Entwickler kennt die verwandte Funktionalität nicht, versteht nicht alle Abhängigkeiten oder hat es einfach eilig. Der Grund ist mangelnder Wissensaustausch über die Codebasis. Lösungen: Code-Review mit Entwicklern aus anderen Modulen, Pair Programming, Architekturdokumentation. Der Bus-Faktor eines Projekts ist umgekehrt proportional zur Anzahl der dokumentierten Architekturentscheidungen.
Regressionstests sind der Prozess der erneuten Ausführung vorhandener Tests nach jeder Änderung, um zu überprüfen, ob alte Funktionalität nicht beschädigt wurde. Es ist die einzige Möglichkeit sicherzustellen, dass eine neue Änderung bestehenden Code nicht beeinträchtigt hat. Ohne Regressionstests ist jede Veröffentlichung eine Lotterie: Der Entwickler hofft, nichts beschädigt zu haben, kann es aber nicht bestätigen.
Manuelle Regressionstests sind der teuerste und ineffektivste Ansatz. Mit dem Wachstum eines Projekts steigt die Anzahl der Regressionstestszenarien linear, während die manuelle Ausführungszeit exponentiell wächst. Nach 2–3 Jahren Entwicklung kann eine manuelle Regression 2–3 Wochen dauern, was häufige Veröffentlichungen unmöglich macht. Die einzige Lösung ist Automatisierung.
Automatisierte Regressionstests werden nach der Testpyramide in Ebenen unterteilt:
Laut Google Testing Blog beträgt das optimale Verhältnis 70% Unit-Tests, 20% Integrationstests, 10% E2E. Eine Abweichung von diesem Verhältnis verringert die Effektivität von Regressionstests: Zu viele E2E-Tests verlangsamen die Pipeline, zu wenige Unit-Tests lassen Mikrofehler unentdeckt.
Die erste Strategie ist die vollständige Regression. Alle Tests des Projekts werden ausgeführt. Der zuverlässigste, aber auch langsamste Ansatz. Geeignet für kleine Projekte (bis zu 10.000 Tests, Laufzeit <30 Minuten). Bei großen Projekten kann eine vollständige Regression Stunden dauern, was die CI/CD-Pipeline unpraktisch macht.
Die zweite Strategie ist die selektive Regression. Es werden nur Tests ausgeführt, die mit dem geänderten Code zusammenhängen. Zur Bestimmung der Beziehungen wird ein Code-Abhängigkeitsgraph verwendet. Werkzeuge: Bazel (Google), Nx (JavaScript), sbt (Scala). Selektive Regression spart 60–80% der Laufzeit, erfordert aber eine genaue Erstellung des Abhängigkeitsgraphen — Fehler führen zu übersehenen Regressionen.
Die dritte Strategie ist die priorisierte Regression. Alle Tests werden nach Priorität geordnet: kritischer Pfad (wichtigste Benutzerszenarien), hohes Risiko (Code mit Fehlerhistorie), geänderter Code (von der Änderung betroffener Code). Die Tests mit der höchsten Priorität werden zuerst ausgeführt — wenn sie bestehen, erhält der Entwickler schnelles Feedback. Zeitlich begrenzte Ausführung: Kritische Tests werden in 10 Minuten überprüft, der Rest läuft im Hintergrund.
Der erste und wichtigste Schritt ist eine Kultur des Testschreibens. Jede Änderung sollte von einem Test begleitet werden, der überprüft, dass die Änderung funktioniert, und einem Test, der überprüft, dass nichts beschädigt wurde. TDD (Test-Driven Development) liefert die besten Ergebnisse: Der Entwickler schreibt zuerst einen fehlschlagenden Test, dann den Code, der ihn besteht. Dies garantiert, dass der Test vor dem Code existiert.
Der zweite Schritt ist eine CI/CD-Pipeline mit obligatorischer Testausführung. Ein Pull-Request kann nicht gemergt werden, bis alle Tests bestanden sind. Tests können aufgrund von Dringlichkeit nicht „übersprungen“ werden — dringende Änderungen durchlaufen eine beschleunigte, aber obligatorische Testsuite. Laut Google DevOps Research haben Teams mit obligatorischem CI/CD 3-mal weniger Regressionen in der Produktion.
Der dritte Schritt ist die Produktionsüberwachung. Selbst die besten Tests garantieren keinen 100%igen Schutz vor Regressionen. Observability-Tools (Sentry, Datadog, New Relic) sollten nach jeder Bereitstellung wichtige Metriken verfolgen: Fehlerrate, Latenz, Durchsatz. Der automatische Rollback bei Überschreitung von Schwellenwerten ist ein Sicherheitsnetz, falls eine Regression doch in die Produktion gelangt.
Der vierte Schritt ist das Code-Review mit Regressionsdenken. Der Reviewer sollte fragen: „Welche anderen Module könnten durch diese Änderung beschädigt werden?“ Es reicht nicht zu überprüfen, dass der Code korrekt ist — man muss überprüfen, dass er keine verwandte Funktionalität beeinträchtigt. Die Code-Review-Checkliste sollte einen Punkt „Regressionsprüfung in verwandten Modulen“ enthalten.
Häufig gestellte Fragen
Eine Regression ist ein Fehler, den es vorher nicht gab. Ein gewöhnlicher Fehler könnte seit der Erstellung der Funktion existiert haben. Eine Regression ist immer an eine bestimmte Änderung gebunden — dies ermöglicht die Verwendung von git bisect, um die Ursache zu finden.
Verwenden Sie git bisect: Geben Sie den Commit an, in dem alles funktionierte, und den Commit, in dem es kaputtging. Git führt eine binäre Suche im Verlauf durch und findet den Commit, der die Regression verursacht hat. Dies funktioniert sogar für große Projekte mit Tausenden von Commits.
Es gibt keine definitive Zahl, aber eine Faustregel: Die Abdeckung der wichtigsten Benutzerabläufe sollte 100% betragen, die Abdeckung aller Funktionen mindestens 70%. Qualität ist wichtiger als Quantität: Ein Test, der einen Grenzfall prüft, ist mehr wert als zehn Tests auf dem Happy Path.
Ja, das nennt man Infrastruktur-Regression. Ein OS-Update, eine Datenbankversionsänderung, eine SSL-Zertifikatsaktualisierung oder eine Webserver-Konfigurationsänderung können funktionierenden Code beschädigen. IaC (Infrastructure as Code) und Infrastrukturtests (Test Kitchen, Terratest) helfen, solche Regressionen zu erkennen.
Beginnen Sie mit einem kritischen Benutzerablauf. Schreiben Sie einen automatischen Test für das wichtigste Szenario (Anmeldung, Bestellabschluss). Zeigen Sie in einer Demo, wie der Test eine Regression aufdeckt. Sobald das Team den Nutzen sieht, erweitern Sie die Abdeckung schrittweise.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch