Zum Hauptinhalt springen
RefoundRefound
Alle Artikel
migrationarchitecturerefactoring

Das Strangler Fig Pattern: Wie man ein Legacy-System modernisiert, ohne das Geschäft zu stoppen

Niclas Kusenbach

Im Jahr 2004 benannte Martin Fowler eine Migrationsstrategie nach einem Baum. Die Würgefeige (Strangler Fig) wächst um einen Wirtsbaum herum und ersetzt ihn langsam — das Original stirbt schließlich und fällt ab, während die Feige von selbst steht. Die Metapher ist exakt: Sie wickeln neue Funktionalität um das alte System, verlagern den Traffic schrittweise auf die neuen Pfade und schicken Legacy-Code erst dann in den Ruhestand, wenn sich sein Ersatz in der Produktion bewährt hat.

Es ist der einzige Migrationsansatz, der bei Systemen, die nicht offline gehen können, durchgängig funktioniert.

Warum Big-Bang-Rewrites scheitern

Der Reiz eines kompletten Rewrites liegt auf der Hand. Ein sauberer Start. Keine Legacy-Einschränkungen. Ein moderner Tech-Stack, Testabdeckung, CI vom ersten Tag an. Die Realität ist jedoch, dass die meisten großen Rewrites zwei- bis dreimal länger dauern als geschätzt, das Budget überschreiten und häufig vor der Fertigstellung abgebrochen werden — was dem Team zwei halb funktionierende Systeme statt eines hinterlässt.

Die Probleme sind struktureller Natur und keine Frage der Teamqualität:

  • Sie bauen blind. Das alte System kodiert jahrelange Geschäftslogik, Sonderfälle und regulatorische Anforderungen, die nie formell dokumentiert wurden. Sie entdecken sie erst, wenn sie fehlen.
  • Das Ziel bewegt sich. Während Sie umschreiben, läuft das Geschäft auf dem alten System weiter. Bis Sie zur Umstellung bereit sind, haben sich die Systeme auseinanderentwickelt.
  • Der Cutover ist ein Single Point of Failure. Egal wie viel Sie testen, der Wechsel von alt zu neu ist ein hochriskanter Moment. Wenn etwas schiefgeht, müssen Sie zurückrollen (Rollback) und verlieren Monate an Arbeit.

Wie die Würgefeige (Strangler Fig) funktioniert

Das Muster besteht aus drei Phasen, die wiederholt werden, bis das alte System verschwunden ist:

1. Eine Naht (Seam) identifizieren

Eine Naht ist eine natürliche Grenze im bestehenden System — ein diskreter Teil der Funktionalität, der extrahiert werden kann, ohne etwas anderes zu ändern. Gute Nähte sind: ein einzelner API-Endpunkt, ein Hintergrundjob, ein Reporting-Modul, ein spezifischer nutzerseitiger Workflow.

Die erste Naht, die Sie auswählen, sollte risikoarm und wertvoll sein. Risikoarm, weil Sie das Muster noch erlernen. Wertvoll, weil es den Ansatz gegenüber den Stakeholdern beweist.

2. Parallel bauen

Schreiben Sie den Ersatz für diese Naht im neuen Stack. Betreiben Sie sowohl die alte als auch die neue Implementierung gleichzeitig. Löschen Sie noch keinen alten Code.

An der Grenze — normalerweise ein API-Gateway, ein Feature Flag oder eine Routing-Schicht — steuern Sie, welche Implementierung jede Anfrage bearbeitet. Zu Beginn gehen 100 % auf den alten Pfad. Sie können den Traffic schrittweise verlagern: 1 %, 10 %, 50 %, 100 %.

         ┌──────────────────────────────┐
         │         API Gateway          │
         └──────────────┬───────────────┘
                        │
            ┌───────────┴───────────┐
            │                       │
     [altes System]           [neuer Service]
     (90% des Traffics)       (10% des Traffics)

3. Validieren, dann in den Ruhestand schicken

Sobald die neue Implementierung 100 % des Traffics abwickelt und für einen vereinbarten Zeitraum in der Produktion stabil läuft, wird der alte Code gelöscht. Nicht archiviert. Gelöscht.

Das ist wichtig. Code, der "für alle Fälle aufbewahrt" wird, wird zu Code, von dem man abhängt, dann zu Code, der gewartet wird, und schließlich zu Legacy-Code, den man in fünf Jahren erneut migrieren muss.

Praktische Bedenken

Datenbank-Migrationen. Der schwierigste Teil jeder Migration ist die Datenschicht. Das Strangler Fig Pattern macht dies nicht einfach — es macht es handhabbar. Während des Parallelbetriebs schreiben beide Systeme in dieselbe Datenbank, oder Sie betreiben eine Synchronisationsschicht zwischen dem alten und dem neuen Schema. Spaltenweise Migrationen mit Tools wie pgroll oder Flyway ermöglichen es Ihnen, das Schema ohne Ausfallzeiten schrittweise weiterzuentwickeln.

Feature Flags. Infrastruktur wie LaunchDarkly, Unleash oder auch nur eine einfache Datenbanktabelle mit Flags gibt Ihnen feingranulare Kontrolle über das Traffic-Routing. Sie bieten Ihnen auch einen sofortigen Not-Aus-Schalter (Kill Switch), falls sich der neue Pfad unerwartet verhält.

Observability (Beobachtbarkeit). Sie benötigen Metriken, Logs und Traces für den alten und den neuen Pfad während des Übergangs. Ohne diese können Sie keine fundierten Entscheidungen darüber treffen, wann Sie den Traffic verlagern. Instrumentieren Sie beide Seiten, bevor Sie auch nur einen Prozentpunkt umlegen.

Teamwissen. Das Team, das das alte System gebaut hat, besitzt Kontext, der nicht im Code steht. Ihre Einbindung in die Migration ist nicht optional — sie macht den Unterschied zwischen einer Migration, die ausgeliefert wird, und einer, die nach sechs Monaten einen kritischen Sonderfall entdeckt.

Wann man die Würgefeige nicht verwenden sollte

Das Muster funktioniert am besten, wenn das System identifizierbare Grenzen hat — eine API-Oberfläche, diskrete Module, trennbare Workflows. Wenn die Legacy-Codebase ein monolithisches Gewirr ist, in dem jede Funktion auf jede andere zugreift, müssen Sie zuerst einige Refactoring-Arbeiten durchführen, um die Nähte zu schaffen. Diese Arbeit lohnt sich; sie ist nicht verschwendet.

Das Strangler Fig Pattern ist auch dann nicht angebracht, wenn das Unternehmen wirklich keine zwei Implementierungen ertragen kann, die gleichzeitig laufen — zum Beispiel, wenn die Kosten für die Wartung der Routing-Schicht die Kosten eines geplanten Ausfalls übersteigen. Für die meisten Produktionssysteme ist diese Sorge jedoch theoretischer Natur. Die meisten Unternehmen haben mehr Toleranz für eine vorsichtige zweigleisige Migration als für das Risiko eines fehlgeschlagenen Cutovers.

Wie eine Migration in der Praxis aussieht

Ein mittelständisches Logistikunternehmen kommt mit einem PHP-Monolithen zu uns: 180.000 Zeilen, keine Tests, zuletzt aktualisiert im Jahr 2019. Sie verarbeiten 3.000 Sendungen pro Tag und dürfen keine Ausfallzeiten haben.

Ich beginne mit der Tracking-API — einer klar definierten Oberfläche, die 40 % des eingehenden Traffics ausmacht. Über sechs Wochen: Neuer Service in Go gebaut, läuft hinter demselben API-Gateway und startet bei 0 % Traffic. Ich füge Observability hinzu, führe Lasttests durch, behebe drei Bugs, die nur unter produktiven Traffic-Mustern auftreten. In Woche sieben verarbeitet der Service 100 % der Tracking-Anfragen. Der PHP-Tracking-Code wird gelöscht.

Ich wiederhole dies für den Workflow zur Erstellung von Sendungen, dann für das Abrechnungsmodul, dann für die Admin-Oberfläche. Achtzehn Monate später wird der PHP-Monolith in den Ruhestand geschickt. Er war an jedem Tag dieser achtzehn Monate live und hat Sendungen verarbeitet. Keine geplante Ausfallzeit. Kein fehlgeschlagener Cutover.

Das ist das Strangler Fig Pattern in der Praxis.


Wenn Sie ein System betreiben, von dem Ihr Unternehmen abhängt, das Sie aber nicht sicher ändern können, ist ein Audit unser Startpunkt — wir kartieren die Nähte, die eine solche Migration möglich machen. Ein konkretes Beispiel für diesen Ansatz bei einem PHP-System finden Sie unter Wie man ohne Ausfallzeiten von PHP 5.6 migriert.