Read in English · Leer en español · Lire en français
Abschnitt 11.6 von 11.8 · Fortgeschritten · 25 Min. Lesezeit
Altsysteme modernisieren: Strangler Fig und schrittweise Migration
Videolektion
5 Min. · auf Englisch gesprochen, mit UntertitelnNur anhören (Audio)
Transkript lesen
Jedes erfolgreiche System wird irgendwann zum Altsystem. In dieser Lektion lernen Sie, warum große Neuentwicklungen so oft scheitern und wie Sie stattdessen Stück für Stück modernisieren.
Stellen Sie sich eine Stadt vor, die eine neue Brücke braucht. Die alte zuerst abzureißen, würde die Stadt jahrelang abschneiden. Also bauen Ingenieure die neue Brücke neben der alten. Sie öffnen sie Spur für Spur und verlagern den Verkehr schrittweise. Hat eine neue Spur ein Problem, fließt der Verkehr einfach für eine Weile wieder über die alte Brücke. Und die alte Brücke reißen sie erst ab, wenn nichts sie mehr nutzt. Die Modernisierung alter Software gelingt am besten genauso.
Ein Altsystem leistet noch etwas Wertvolles, ist aber schwer, riskant oder teuer zu ändern geworden. Die meisten Altsysteme sind erfolgreiche Systeme. Die naheliegende Reaktion ist, von vorn zu beginnen, und das ist meist ein Fehler. Bei einer Big-Bang-Neuentwicklung kommt der Wert erst am Ende, also wartet das Geschäft Monate oder Jahre und verliert die Geduld. Das alte System ändert sich weiter, während das neue gebaut wird, also verschiebt sich das Ziel. Verborgenes Verhalten geht verloren. Alter Code wirkt unordentlich, auch weil er Jahre an Fixes für Situationen enthält, an die sich niemand erinnert. Joel Spolsky hat das im Jahr 2000 am Beispiel der Neuentwicklung bei Netscape beschrieben. Und die Datenmigration und die Umstellung auf das neue System kommen zuletzt, wenn sie am schwierigsten sind. Im Jahr 2018 ging die Plattformmigration der britischen Bank TSB gründlich schief, und die Aufsichtsbehörden verhängten später eine Geldbuße von achtundvierzig Komma sechs fünf Millionen Pfund.
Das Strangler-Fig-Muster, von Martin Fowler benannt, ersetzt ein System Stück für Stück, während es weiterläuft. Erstens abfangen: Setzen Sie eine Routing-Fassade vor das Altsystem. Anfangs schickt sie alles an das alte System. Bauen Sie dann einen Slice von Verhalten, hier die Bestellverfolgung, als neue Komponente neu, und ändern Sie eine Routing-Regel, um ihn dorthin zu leiten. Verhält sie sich falsch, schalten Sie die Regel zurück. Wiederholen Sie das Stück für Stück, mit vorübergehender Datensynchronisation, bis im alten System nur noch die Abrechnung übrig ist. Schließlich wird das Altsystem abgeschaltet. Fassade, Adapter und Sync-Jobs sind Übergangsarchitektur, gebaut, um weggeworfen zu werden. Messen Sie den Fortschritt also daran, was abgeschaltet wurde.
Daten sind der schwierigste Teil. In jeder Phase muss genau ein System das System of Record sein. Anfangs besitzt das Altsystem die Bestellungen, und Change Data Capture kopiert seine Zeilen in den neuen Speicher, über einen Anti-Corruption Layer, der sie übersetzt. Später kehrt sich die Richtung um. Der neue Bestellservice besitzt die Bestellungen, und seine Events werden für Module wie die Abrechnung, die noch nicht umgezogen sind, zurück in Legacy-Zeilen übersetzt. Daten fließen jeweils nur in eine Richtung.
So hat die Plattform ihr Bestellmodul aus dem Monolithen herausgelöst. Erstens Ergebnisse festlegen: tägliche, unabhängige Bestell-Releases, das Dreifache der heutigen Freitagsspitze und keine verlorenen oder doppelten Bestellungen. Aufgezeichnete Produktionsanfragen wurden zu Tests, die das aktuelle Verhalten samt seinen Eigenheiten festhalten. Zweitens ein API-Gateway vor den Monolithen setzen, das alles an ihn weiterleitet. Es ändert kein Verhalten, und das Team übt das Umschalten von Routen. Drittens den neuen Service im Schatten bauen, während das Altsystem das System of Record bleibt. Im Parallelbetrieb beginnt die Abweichungsrate bei etwa zwei Prozent und wird unter null Komma null eins Prozent gedrückt, bevor es weitergeht. Viertens die Lesezugriffe der Bestellverfolgung verlagern: fünf Prozent des Verkehrs, dann fünfundzwanzig, dann alles. Das Rollback ist eine einzige Routing-Regel. Fünftens die Schreibzugriffe verlagern, Stadt für Stadt: eine Pilotstadt, dann innerhalb von sechs Wochen jede Stadt. Ein Adapter verwandelt die Events des neuen Service in Legacy-Zeilen, damit Abrechnung und Berichtswesen weiterarbeiten. Schließlich drei Jahre Historie migrieren. Wenn die Legacy-Bestelltabellen dreißig Tage lang keine Lesezugriffe hatten, entfernen Sie den Adapter, die Pipeline und den alten Code. Erst dann ist die Migration abgeschlossen.
Das Strangler-Fig-Muster wirkt am Rand, wo sich Anfragen routen lassen. Branch by Abstraction wirkt tief in einer einzigen Codebasis. Legen Sie eine Schnittstelle vor die alte Komponente, stellen Sie die Aufrufer darauf um, bauen Sie die neue Implementierung dahinter, schalten Sie schrittweise um und löschen Sie dann den alten Code. Ein Parallelbetrieb weist nach, dass sich der neue Code wie der alte verhält. Beide erhalten dieselben Eingaben, aber das alte Ergebnis wird weiterhin an den Nutzer zurückgegeben, und Unterschiede werden festgehalten. Der neue Pfad darf keine Nebenwirkungen haben, etwa eine Karte doppelt zu belasten.
Der vollständige Abschnitt behandelt die sieben Rs, Dark Launching und die sichere Migration von Daten. Als Nächstes geht es in Abschnitt elf Punkt sieben um Führung und Einfluss. Bis gleich.
Was Sie wissen müssen
- Ein Altsystem liefert noch Wert, ist aber schwer, riskant oder teuer zu ändern; modernisieren Sie aus geschäftlichen Gründen wie blockierten Änderungen, verlorenem Support, knappen Fachkenntnissen oder Kosten, nicht aus Mode.
- Big-Bang-Neuentwicklungen scheitern, weil der Wert spät kommt, sich das Ziel verschiebt, verborgenes Verhalten verloren geht und Datenmigration und Umstellung zuletzt kommen; Spolskys Essay von 2000 und die Migration von TSB 2018 sind die klassischen Warnungen.
- Die sieben Rs (Retire, Retain, Rehost, Relocate, Repurchase, Replatform, Refactor oder Re-architect) geben jeder Anwendung in einem Portfolio eine bewusste Strategie; Neubau ist nur eine Option.
- Das Strangler-Fig-Muster (Fowler, 2004) leitet den Verkehr durch eine Fassade und verlagert Verhalten Stück für Stück in neue Komponenten, bis das Altsystem abgeschaltet werden kann; Branch by Abstraction leistet dasselbe innerhalb einer Codebasis.
- Ein Parallelbetrieb vergleicht alte und neue Ergebnisse in der Produktion, während das alte Ergebnis maßgeblich bleibt; Dark Launching belastet neue Pfade mit echter Last, ohne dass Nutzer sie sehen.
- Daten brauchen in jeder Phase ein einziges System of Record; Change Data Capture, Event Interception und Anti-Corruption Layers halten Systeme im Gleichschritt, und die Historie wird in geprobten, abgeglichenen Kohorten migriert.
- Übergangsarchitektur ist ein bewusster, vorübergehender Aufwand; eine Migration ist erst abgeschlossen, wenn das alte System und der Übergangscode entfernt sind.
Was ein System zum Altsystem macht
Ein Altsystem ist nicht einfach ein altes System. Es ist ein System, das noch etwas Wertvolles leistet, oft etwas, ohne das die Organisation nicht arbeiten könnte, das aber schwer, riskant oder teuer zu ändern geworden ist. Michael Feathers gab in Working Effectively with Legacy Code (2004) eine bewusst provokante Definition: Legacy-Code ist Code ohne Tests, weil ohne Tests jede Änderung ein Glücksspiel ist. Weitere häufige Ursachen sind knappe Fachkenntnisse (eine Sprache oder Plattform, die nur noch wenige beherrschen), nicht mehr unterstützte Technologie (ein Betriebssystem oder eine Datenbank nach dem Support-Ende des Herstellers), verlorenes Wissen (die Personen, die das Design verstanden, sind gegangen) und angesammelte technische Schulden in der Struktur selbst, die teure Art, die in Abschnitt 1.7 beschrieben ist.
Man sollte offen sagen, dass die meisten Altsysteme erfolgreiche Systeme sind. Sie haben überlebt, weil sie funktionierten und Geld verdienten. Dieser Erfolg ist auch der Grund, warum sie schwer zu ersetzen sind: Jahre realer Nutzung haben Tausende kleiner Entscheidungen, Fixes und Geschäftsregeln eingebettet, die nirgends aufgeschrieben sind, außer im Code und in den Gewohnheiten der Menschen, die ihn nutzen.
Modernisierung braucht daher einen Grund, der sich in geschäftlichen Begriffen ausdrücken lässt. Gute Gründe sind: Kosten oder Risiko von Änderungen blockieren wichtige Geschäftsziele; die Plattform verliert den Herstellersupport oder Sicherheitspatches; die Fachkenntnisse für ihre Wartung sind bald nicht mehr verfügbar; die Betriebskosten stehen in keinem Verhältnis zum Wert; oder das System kann neue Anforderungen an Skalierung, Verfügbarkeit oder Regulierung nicht erfüllen. Schwache Gründe sind Mode, Langeweile und der Glaube, neue Technologie sei automatisch besser. Abschnitt 11.8 zeigt, wie aus einem guten Grund ein Business Case wird.
Warum Neuentwicklungen scheitern
Die naheliegende Reaktion auf ein schmerzhaftes altes System ist, von vorn zu beginnen. Im April 2000 veröffentlichte Joel Spolsky den Essay „Things You Should Never Do, Part I“ und argumentierte, die Entscheidung, von Grund auf neu zu schreiben, gehöre zu den schlimmsten strategischen Fehlern, die ein Softwareunternehmen machen kann. Sein Beispiel war Netscape, dessen Browser fast drei Jahre lang ohne großes Release blieb, während der Code neu geschrieben wurde, und in dieser Zeit eroberten Wettbewerber den Markt. Sein Kernpunkt betraf Wissen: Alter Code wirkt unordentlich, auch weil er Jahre an Bugfixes für Situationen enthält, an die sich niemand erinnert, und wer ihn wegwirft, wirft auch diese Fixes weg.
Die Gründe, warum eine Big-Bang-Neuentwicklung scheitert, sind inzwischen gut verstanden. Der Wert kommt erst am Ende, sodass das Geschäft Monate oder Jahre wartet und die Geduld verliert. Das alte System ändert sich weiter, während das neue gebaut wird; das Ziel verschiebt sich also, und die Organisation friert entweder das alte System (und damit das Geschäft) ein oder bezahlt dafür, jede Änderung zweimal umzusetzen. Verborgenes Verhalten wird übersehen, weil niemand jede Regel auflisten kann, die das alte System durchsetzt. Schätzungen sind optimistisch, weil die sichtbaren Features nur einen Bruchteil der tatsächlichen Arbeit ausmachen. Die Datenmigration und die Umstellung werden bis zum Ende aufgeschoben, wenn sie am schwierigsten und riskantesten sind. Und das Team spaltet sich in eine angesehene Gruppe für das neue System und eine demoralisierte Gruppe für das alte. Fred Brooks’ Second-System-Effekt aus The Mythical Man-Month (1975) fügt ein weiteres Risiko hinzu: Wer ein bekanntes System ersetzt, neigt dazu, das neue mit jeder Idee zu überladen, die im alten keinen Platz hatte.
Große Migrationen, die in einem Schritt umstellen, können auch sehr öffentlich scheitern. Im April 2018 verlagerte die britische Bank TSB ihre Kundendaten und Dienste auf eine neue IT-Plattform; die Plattform hatte sofort ernste Probleme, alle Filialen und ein erheblicher Teil der 5,2 Millionen Kunden waren betroffen, und die Bank kehrte erst im Dezember 2018 zum Normalbetrieb zurück. Im Dezember 2022 verhängten die Financial Conduct Authority und die Prudential Regulation Authority gegen TSB Geldbußen von insgesamt £48,65 Millionen, weil die Bank die Migration nicht angemessen geplant, organisiert und gesteuert und die Risiken ihres ausgelagerten Dienstleisters nicht beherrscht hatte.
Nicht alle Fachleute halten Neuentwicklungen für immer falsch. Eine Neuentwicklung kann die richtige Wahl sein, wenn das System klein ist, sein Verhalten gut spezifiziert und durch Tests abgedeckt ist, die Plattform wirklich in einer Sackgasse steckt oder sich das Geschäft selbst so stark verändert hat, dass das alte Verhalten nicht mehr gewünscht ist. Selbst dann werden die sichersten Neuentwicklungen schrittweise geliefert, mit altem und neuem System nebeneinander, und genau darum geht es im Rest dieses Abschnitts.
Migrationsstrategien: die Rs
Wenn eine Organisation ein ganzes Anwendungsportfolio überprüft, etwa vor dem Auszug aus einem Rechenzentrum, braucht sie ein Vokabular, um zu entscheiden, was mit jeder Anwendung geschehen soll. Gartner beschrieb Anfang der 2010er-Jahre fünf solcher Optionen. 2016 veröffentlichte Stephen Orban von AWS sechs Strategien, und AWS ergänzte später eine siebte; daraus ergeben sich die sieben Rs, die heute in den Migrationsleitfäden von AWS verwendet werden. Andere Cloud-Anbieter und Beratungen nutzen ähnliche Listen mit leicht abweichenden Namen. Die Liste ist vor allem als Erinnerung nützlich, dass Neubau nur eine von mehreren Optionen ist, und meist die teuerste.
Die sieben Rs (nach AWS Prescriptive Guidance)
| Strategie | Was sie bedeutet | Passt, wenn | Architekturänderung |
|---|---|---|---|
| Retire | Die Anwendung abschalten und ihre Daten archivieren | Niemand nutzt sie, oder ihre Funktion ist anderswo doppelt vorhanden | Keine; Abhängigkeiten von ihr entfernen |
| Retain | Sie vorerst belassen, wo sie ist, und später erneut prüfen | Kein dringender Grund für einen Umzug, oder andere Arbeit hat Vorrang | Vorerst keine |
| Rehost (Lift and Shift) | Sie unverändert auf neue Infrastruktur verlagern, etwa auf virtuelle Maschinen in der Cloud | Der Haupttreiber ist, ein Rechenzentrum schnell zu verlassen | Keine; Probleme ziehen mit um |
| Relocate | Ganze Plattformen verlagern, etwa eine virtualisierte Umgebung, ohne die Anwendungen zu ändern | Am Ziel gibt es eine kompatible Plattform | Keine |
| Repurchase | Sie durch ein Produkt ersetzen, oft Software as a Service | Die Fähigkeit ist generisch, und es gibt ein gutes Produkt | Integration und Datenmigration; Verlust von Anpassungen |
| Replatform | Sie mit einigen gezielten Optimierungen verlagern, etwa einer Managed Database | Günstige Verbesserungen sind ohne Neuentwurf möglich | Klein und lokal |
| Refactor oder Re-architect | Sie neu gestalten, oft schrittweise, um neue Qualitäts- oder Geschäftsanforderungen zu erfüllen | Die Fähigkeit ist zentral, ändert sich oft, und ihre Struktur blockiert Änderungen | Groß; nutzen Sie die schrittweisen Muster weiter unten |
Entscheidungshilfe
Entscheidungshilfe: eine Strategie für eine Anwendung wählen
Stellen Sie diese Fragen für jede Anwendung im Portfolio, in dieser Reihenfolge. Die Antworten kommen aus Nutzungsdaten, vom fachlich Verantwortlichen und aus der Architektur, nicht aus technologischen Vorlieben.
- Braucht noch jemand, was diese Anwendung leistet?JaWeiter zur nächsten Frage.NeinLegen Sie sie still (Retire), nachdem Sie alle Daten archiviert haben, die Sie gesetzlich aufbewahren müssen.
- Gibt es ein Produkt, das den Bedarf deckt, für eine Fähigkeit, die das Unternehmen nicht vom Wettbewerb abhebt?JaErwägen Sie Repurchase, und planen Sie Budget für Integration und Datenmigration ein.NeinWeiter zur nächsten Frage.
- Gibt es einen dringenden Grund, jetzt zu handeln, etwa Support-Ende, Auszug aus einem Rechenzentrum, Kosten oder blockierte Änderungen?JaWeiter zur nächsten Frage.NeinBehalten Sie sie (Retain), und legen Sie einen Termin fest, um die Entscheidung erneut zu prüfen.
- Liegt das Hauptproblem darin, wo sie läuft, statt darin, wie sie gebaut ist?JaWählen Sie Rehost oder Relocate, oder Replatform, wo ein Managed Service günstige Verbesserungen bringt.NeinWeiter zur nächsten Frage.
- Ist sie eine zentrale Fähigkeit, deren Struktur häufige, wertvolle Änderungen blockiert?JaWählen Sie Refactor oder Re-architect, schrittweise, mit dem Strangler-Fig-Muster und Branch by Abstraction.NeinWählen Sie Replatform und dämmen Sie sie ein: Tests ergänzen, die schlimmsten Hotspots beheben und einen großen Neuentwurf vermeiden.
Das Strangler-Fig-Muster
Martin Fowler benannte das am weitesten verbreitete schrittweise Muster nach den Würgefeigen (Strangler Figs), die er 2001 in den Regenwäldern von Queensland sah: Kletterpflanzen, die in den Ästen eines Wirtsbaums keimen, bis zum Boden hinunterwachsen und den Wirt allmählich umschließen, bis er abstirbt und die Feige allein steht. Er beschrieb es erstmals 2004 als Strangler Application, benannte es später in Strangler Fig Application um und überarbeitete den Artikel 2024 grundlegend. Die Softwareversion, meist einfach Strangler Fig genannt, hält das alte System am Laufen, während neue Komponenten um es herum wachsen und sein Verhalten Stück für Stück übernehmen, bis das alte System nichts mehr tut und abgeschaltet werden kann.
Der Mechanismus hat drei Teile. Erstens wird vor das Altsystem eine Abfangschicht gesetzt: ein Reverse Proxy, ein API-Gateway oder eine Routing-Fassade, durch die alle Anfragen laufen. Anfangs schickt sie alles an das alte System und ändert nichts. Zweitens wird ein Slice von Verhalten entlang einer Naht gewählt (in Feathers’ Begriffen eine Stelle, an der sich Verhalten umleiten lässt, ohne viel vom alten Code zu ändern), als neue Komponente neu gebaut und durch Ändern einer Routing-Regel aktiviert, meist zunächst für einen kleinen Teil des Verkehrs. Drittens wiederholt sich der Vorgang, bis das Altsystem nichts mehr verarbeitet; dann wird es stillgelegt. Die ganze Zeit über macht die Routing-Schicht jeden Schritt umkehrbar: Verhält sich die neue Komponente falsch, wird die Regel zurückgeschaltet.
Der Code, der Alt und Neu nebeneinander existieren lässt (die Routing-Fassade, Adapter, Jobs zur Datensynchronisation), ist Übergangsarchitektur: Software, die gebaut wird, um weggeworfen zu werden. Teams sträuben sich oft dagegen, sie zu schreiben, weil es sich nach Verschwendung anfühlt. Fowlers Argument und die Erfahrung dahinter lauten, dass dieser Aufwand ein geringeres Risiko und viel früheren Nutzen erkauft, was fast immer ein guter Tausch ist. Die eigentliche Gefahr ist das Gegenteil: eine Migration, die auf halbem Weg steckenbleibt und beide Systeme samt dem gesamten Übergangscode auf unbestimmte Zeit in der Produktion zurücklässt. Jeder Slice sollte daher etwas aus dem alten System entfernen, und der Fortschritt sollte daran gemessen werden, was abgeschaltet wurde, nicht nur daran, was gebaut wurde.
Ian Cartwright, Rob Horn und James Lewis von Thoughtworks haben eine Familie verwandter Techniken als Patterns of Legacy Displacement auf martinfowler.com dokumentiert (ab 2021). Sie beschreiben die Arbeit als vier Tätigkeiten, die keine feste Reihenfolge haben: die gewünschten Ergebnisse verstehen, das Problem in Teile zerlegen, diese Teile schrittweise liefern und die Organisation so verändern, dass das neue System nicht so verfällt wie das alte. Sie hinterfragen auch den Reflex, jedes Feature nachzubauen: Ein Großteil des Verhaltens eines Altsystems wird vielleicht nicht mehr gebraucht, und das herauszufinden ist günstiger, als es neu zu bauen.
Eine Strangler-Fig-Migration in vier Phasen
Branch by Abstraction
Das Strangler-Fig-Muster wirkt am Rand eines Systems, wo sich Anfragen routen lassen. Manche Ersetzungen müssen tief in einer einzigen Codebasis stattfinden: der Austausch eines Persistenz-Frameworks, einer Preis-Engine oder einer selbst gebauten Messaging-Schicht, die Hunderte von Aufrufstellen nutzen. Branch by Abstraction, ein Name, den Paul Hammant prägte, als er für Trunk-Based Development warb, und den Martin Fowler 2014 beschrieb, leistet das ohne langlebigen Branch in der Versionsverwaltung. Das System bleibt in jedem Schritt releasefähig, sodass sich die Änderung über Wochen oder Monate verteilen lässt, während auf derselben Hauptlinie andere Arbeit weitergeht.
Branch by Abstraction, Schritt für Schritt
- Eine Abstraktion einführenLegen Sie eine Schnittstelle vor die zu ersetzende Komponente, mit einer Implementierung, die einfach an den bestehenden Code delegiert. Am Verhalten ändert sich nichts.
- Aufrufer auf die Abstraktion umstellenÄndern Sie die Aufrufstellen nach und nach so, dass sie die Schnittstelle statt direkt die alte Komponente nutzen. Jede Änderung ist klein und wird normal released.
- Die neue Implementierung bauenSchreiben Sie den Ersatz hinter derselben Schnittstelle und testen Sie ihn mit denselben Tests und idealerweise in einem Parallelbetrieb gegen das Produktionsverhalten.
- Schrittweise umschaltenNutzen Sie ein Feature-Flag oder eine Konfiguration, um einige Aufrufer oder einige Kunden an die neue Implementierung zu leiten, und erhöhen Sie den Anteil, während das Vertrauen wächst.
- Die alte Implementierung entfernenSobald nichts mehr den alten Code nutzt, löschen Sie ihn, und entfernen Sie auch die Abstraktion, wenn sie ihren Platz nicht mehr rechtfertigt.
Gleichwertigkeit nachweisen: Parallelbetrieb und Dark Launching
Die schwierigste Frage jeder Migration ist, ob sich die neue Implementierung wirklich wie die alte verhält. Ein Parallelbetrieb beantwortet sie mit Belegen aus der Produktion: Beide Implementierungen erhalten dieselben Eingaben, die alte bleibt maßgeblich, und ihre Antwort wird an den Nutzer zurückgegeben, während die beiden Ergebnisse verglichen und alle Abweichungen festgehalten werden. GitHub veröffentlichte 2016 genau dafür seine Bibliothek Scientist, die einen Control- und einen Candidate-Pfad nebeneinander ausführt und Abweichungen meldet; ähnliche Werkzeuge gibt es für viele Sprachen, und die Technik lässt sich leicht von Hand schreiben, wie die Skizze unten zeigt.
Dark Launching, das Fowler als Aufruf neuen Backend-Verhaltens in der Produktion beschreibt, ohne dass Nutzer seine Auswirkungen sehen, ist eng verwandt. Es dient vor allem dazu, Performance und Stabilität des neuen Pfads unter echter Last zu testen, bevor ein Nutzer davon abhängt, während sich ein Parallelbetrieb auf die Korrektheit konzentriert. Beide unterscheiden sich von einem Canary Release, bei dem ein kleiner Teil der Nutzer das neue Verhalten tatsächlich erhält.
Beide Techniken haben Tücken. Der Candidate darf keine nach außen sichtbaren Nebenwirkungen haben: Er darf keine zweite E-Mail senden, keine Karte doppelt belasten und nicht in die gemeinsame Datenbank schreiben; Parallelbetrieb ist daher bei Lesezugriffen und Berechnungen am einfachsten, und Schreibzugriffe brauchen eine Sandbox oder einen separaten Speicher. Vergleiche müssen legitime Unterschiede wie Zeitstempel, Reihenfolge und Gleitkommaformatierung ignorieren, sonst geht der Abweichungsbericht im Rauschen unter. Und beide Pfade auszuführen kostet Kapazität, weshalb der Anteil des stichprobenartig geprüften Verkehrs oft begrenzt wird.
def quote_delivery_fee(order):
old = legacy_pricing.quote(order) # still authoritative
try:
new = new_pricing.quote(order) # must have no side effects
if new.amount_pence != old.amount_pence:
metrics.increment("pricing.mismatch")
log.info("pricing mismatch", order_id=order.id,
old=old.amount_pence, new=new.amount_pence)
else:
metrics.increment("pricing.match")
except Exception:
metrics.increment("pricing.candidate_error") # never hurts the user
return oldDaten: der schwierigste Teil
Anfragen zu routen ist relativ einfach; Daten zu verlagern und konsistent zu halten, während zwei Systeme gemeinsam für sie zuständig sind, ist der Punkt, an dem die meisten Migrationen kämpfen. Die erste Regel ist die aus Kapitel 7 zur Datenhoheit: Für jede Art von Daten muss das Team in jeder Phase der Migration sagen können, welches System das System of Record ist, welche Kopien existieren und wie veraltet jede sein darf. Das Diagramm unten zeigt den typischen Wechsel: Zuerst besitzt das Altsystem die Daten, und die neue Komponente liest eine Kopie; später besitzt die neue Komponente sie, und das Altsystem erhält eine Kopie für die Module, die sie noch brauchen.
Change Data Capture ist der übliche Weg, Daten aus einer Legacy-Datenbank zu kopieren, ohne den Legacy-Code zu ändern: Ein Werkzeug wie Debezium liest das Transaktionslog der Datenbank und veröffentlicht jede Änderung als Event, derselbe Mechanismus, den Abschnitt 6.7 zum Weiterleiten einer Outbox verwendet hat. Event Interception, eines der Muster zur Ablösung von Altsystemen, setzt am anderen Ende an: Es fängt zustandsändernde Anfragen oder Nachrichten auf dem Weg in das Altsystem ab und leitet Kopien (oder später die Originale) an die neue Komponente weiter. In beiden Fällen gilt: Lassen Sie niemals beide Systeme unabhängig voneinander Schreibzugriffe auf dieselben Daten mit Zwei-Wege-Synchronisation annehmen, es sei denn, Sie haben keine Alternative; Konflikte sind dann nahezu garantiert.
Legacy-Daten passen selten zum neuen Modell. Statuscodes sind Ganzzahlen mit undokumentierter Bedeutung, eine Tabelle dient drei Zwecken, und Datumsangaben sind in Ortszeit gespeichert. Ein Anti-Corruption Layer, das Muster aus dem Domain-Driven Design, das ein Modell vor dem eines Nachbarn schützt, übersetzt zwischen beiden, damit die Eigenheiten des Altsystems nicht in den neuen Code durchsickern. Er gehört zur Übergangsarchitektur und sollte mit dem Altsystem verschwinden.
Die abschließende Datenmigration, also die Übernahme der Historie und die Umstellung, verdient einen eigenen Plan. Profilieren Sie zuerst die Legacy-Daten, um Duplikate, verwaiste Datensätze und ungültige Werte zu finden. Schreiben Sie die Abbildung explizit auf und testen Sie sie an einer vollständigen Kopie in Produktionsgröße. Gleichen Sie nach jedem Lauf mit Zeilenzahlen, Summen der Geldspalten und Prüfsummen ab, nicht mit Stichproben. Proben Sie die Umstellung, messen Sie ihre Dauer und schreiben Sie das Rollback auf. Migrieren Sie nach Möglichkeit in Kohorten (eine Region, ein Kundensegment, eine Produktlinie) statt alles auf einmal, damit ein Problem nur einen Bruchteil der Kunden trifft und Sie vor der nächsten Kohorte etwas daraus lernen.
Das System of Record wechselt während der Migration
Lehren aus Kill It with Fire
Marianne Bellottis Kill It with Fire: Manage Aging Computer Systems (and Future Proof Modern Ones) (No Starch Press, 2021) schöpft aus ihrer Erfahrung mit der Modernisierung von Behörden- und Unternehmenssystemen, darunter Mainframe-Landschaften. Mehrere seiner Themen lohnt es, in jede Modernisierung mitzunehmen.
Modernisierung sollte durch den Wert, den sie liefert, und das Risiko, das sie beseitigt, begründet werden, nicht durch das Alter der Technologie; alte Technologie, die gut verstanden ist und funktioniert, kann der risikoärmste Teil der Landschaft sein. Technologieentscheidungen verlaufen in Zyklen; ein Team, das ein System nur ersetzt, weil sein Design aus der Mode ist, baut daher wahrscheinlich das Altsystem der nächsten Generation. Die schrittweise Verbesserung des bestehenden Systems ist meist sicherer als ein Ersatz, und eine vollständige Neuentwicklung braucht eine ungewöhnlich starke Begründung. Schließlich ist Modernisierung ebenso ein organisatorisches wie ein technisches Problem: Sie braucht sichtbare frühe Erfolge, um Schwung und Moral zu erhalten, klare Kriterien dafür, wann sie abgeschlossen ist, und Aufmerksamkeit für die Anreize, die das alte System verfallen ließen, sonst folgt ihm das neue.
Ausgearbeitetes Beispiel
Ausgearbeitetes Beispiel: das Bestellmodul in sechs Schritten aus dem Monolithen herauslösen
Dieses Beispiel blickt darauf zurück, wie die Essenslieferplattform zu dem separaten Bestellservice kam, der in Abschnitt 11.1 bewertet wurde. Früher enthielt der ursprüngliche Monolith der Plattform ein Bestellmodul, das das Aufgeben, Ändern, Stornieren und Verfolgen von Bestellungen abwickelte. Änderungen an Bestellungen waren zum Hauptengpass geworden: Sie erforderten ein Deployment des gesamten Monolithen, und die Spitzen am Freitagabend verlangsamten jedes andere Feature. Vierzehn andere Module, darunter Abrechnung, Berichtswesen und die Support-Tools, lasen die Bestelltabellen direkt. Das Team plante eine schrittweise Migration zu einem separaten Bestellservice.
| 1. Ergebnisse festlegen und die Naht finden | Ergebnisse: Bestelländerungen täglich und unabhängig deploybar; das Dreifache der heutigen Freitagsspitze, ohne andere Features zu beeinträchtigen; keine verlorenen oder doppelten Bestellungen. Die Analyse der Abfragelogs bestätigt, dass nur das Bestellmodul in die Bestelltabellen schreibt und dass der gesamte Bestellverkehr über die Endpoints /api/orders hereinkommt. Das Team zeichnet mehrere Wochen Produktionsanfragen auf und macht daraus Charakterisierungstests, die das aktuelle Verhalten samt seinen Eigenheiten festhalten. |
|---|---|
| 2. Die Routing-Fassade einfügen | Vor den Monolithen wird ein API-Gateway gesetzt, das anfangs alles an ihn weiterleitet. Für sich allein released, ändert es kein Verhalten; das Team prüft die zusätzliche Latenz (wenige Millisekunden beim 95. Perzentil) und übt das Umschalten von Routen. Rollback: das Gateway aus dem Pfad nehmen. |
| 3. Den neuen Service im Schatten bauen | Der neue Bestellservice hat einen eigenen Speicher. Das Altsystem bleibt das System of Record: Change Data Capture kopiert Bestellzeilen hinüber, über einen Anti-Corruption Layer, der ganzzahlige Statuscodes und Datumsangaben in Ortszeit auf das neue Modell abbildet. Lese-Endpoints laufen als Parallelbetrieb gegen die Legacy-Antworten; die Abweichungsrate beginnt bei etwa 2 % (Rundung von Gebühren, Zeitzonen) und wird unter 0,01 % gedrückt, bevor es weitergeht. |
| 4. Lesezugriffe verlagern | Lesezugriffe der Bestellverfolgung werden zu 5 %, dann zu 25 %, dann für den gesamten Verkehr an den neuen Service geleitet, wobei in jeder Phase Fehlerraten und Latenz beobachtet werden. Rollback: eine Routing-Regel. |
| 5. Schreibzugriffe verlagern, Stadt für Stadt | Der neue Service wird zum System of Record für Bestellungen in einer Pilotstadt, dann innerhalb von sechs Wochen für jede Stadt. Er veröffentlicht OrderPlaced- und OrderCancelled-Events über eine Transactional Outbox; ein Übergangsadapter übersetzt sie in Zeilen im Legacy-Format, damit Abrechnung und Berichtswesen unverändert weiterarbeiten. Ein täglicher Abgleich vergleicht Anzahlen und Geldsummen zwischen den beiden Speichern. Diese Module wechseln dann in den folgenden Quartalen zu den Events oder zur Bestell-API. |
| 6. Historie migrieren und ablösen | Drei Jahre historischer Bestellungen werden in gedrosselten Batches kopiert und über Anzahlen und Prüfsummen abgeglichen. Als die Audit-Logs der Datenbank 30 Tage lang keine Lesezugriffe auf die Legacy-Bestelltabellen zeigen, entfernt das Team den Adapter, die Change-Data-Capture-Pipeline und die alten Routing-Regeln und löscht dann den Bestellcode und die Tabellen aus dem Monolithen. Erst an diesem Punkt ist die Migration abgeschlossen. |
Jeder Schritt liefert etwas Nutzbares oder beseitigt Risiko, und jeder Schritt außer dem letzten hat ein günstiges Rollback.
Das System of Record für Bestellungen ist in jeder Phase eindeutig, und Daten fließen jeweils nur in eine Richtung.
Die Übergangsarchitektur wird geplant, gemessen und bewusst entfernt; Erfolg wird daran gemessen, was abgeschaltet wurde.
Prüfen Sie Ihr Verständnis
Warum verliert eine Big-Bang-Neuentwicklung tendenziell Verhalten, auf das Nutzer angewiesen sind?
Weil Jahre an Bugfixes und Geschäftsregeln nur im alten Code und in den Gewohnheiten der Nutzer existieren; niemand kann sie alle auflisten, sodass eine Neuentwicklung nach Spezifikation einige davon übersieht.
Was trägt die Routing-Fassade zu einer Strangler-Fig-Migration bei?
Sie lässt den Verkehr Stück für Stück verlagern und, wenn etwas schiefgeht, wieder zurückverlagern, ohne das Altsystem für jeden Schritt zu ändern.
Was ist Übergangsarchitektur, und warum sollte man ihre Entfernung verfolgen?
Vorübergehender Code wie Routing-Regeln, Adapter und Synchronisationsjobs, der Alt und Neu nebeneinander bestehen lässt; wer seine Entfernung verfolgt, schützt sich vor einer Migration, die steckenbleibt, während beide Systeme auf unbestimmte Zeit laufen.
Wann könnte Repurchase eine bessere Wahl sein als Refactoring?
Wenn die Fähigkeit generisch statt differenzierend ist und es ein gutes Produkt gibt, sodass ein Neubau Aufwand dort verbrauchen würde, wo das Unternehmen nicht im Wettbewerb steht.
Quellen und weiterführende Literatur
Haben Sie eine Frage zu diesem Abschnitt? ArchitectBot, der KI-Lernassistent des Kurses, antwortet zuerst aus dem Kurs.
Fragen: „Unser Kernsystem ist ein 15 Jahre alter Monolith, den nur zwei Personen verstehen. Wie entscheide ich, ob wir ihn neu schreiben, und wenn nicht, wo sollte eine schrittweise Migration beginnen?“