8 Min. Lesezeit

Es gibt sie tatsächlich noch, die Legacy-Systeme, die in Cobol, PL/1, Smalltalk oder anderen einstmals modernen Programmiersprachen entwickelt wurden, die heute kaum noch jemand lernt oder programmieren möchte. Alte, große Softwaresysteme, welche über die Jahre immer komplexer werden, während das Wissen darüber und über die verwendeten Sprachen und Technologien sinkt. Mit der Zeit wird der Druck, solche Legacy-Systeme zu modernisieren, immer größer, bis das Management endlich entscheidet, die nötigen Gelder freizugeben und Migrationsprojekte zu starten. Doch wann ist eine automatisierte Modernisierung deines Legacy-Systems sinnvoll?  

Fotolia_138260626_M_bearbeitet_SHE_Blog_Kann die Modernisierung deines Legacy Systems automatisiert werden_960x460px

Häufig ist es nicht sinnvoll, ein komplett neues System zu entwickeln. Denn Unmengen an fachlichen Entscheidungen sind tief im Programmcode verankert. Dokumentation? Fehlanzeige! Hinreichende Testabdeckung? Natürlich nicht. Migrationsprojekte sind außerdem Langläufer, und während einer Migration entwickelt sich das zu migrierende System ständig weiter.

Und wohin soll eigentlich migriert werden? Ist Java in 10 Jahren noch aktuell? Bereits vor über 10 Jahren wurde behauptet, dass Java das neue Cobol sei. Noch ist das nicht so, doch andere Programmiersprachen, Architekturansätze und Frameworks buhlen um die Gunst der Software-Entwickler. Das zeigt sich auch am Arbeitsmarkt. Wenn Altsysteme tatsächlich noch weiter gewartet werden müssen, ist es schwer, Entwickler zu finden, die das noch können oder wollen. Und wenn, dann lassen sie es sich teuer bezahlen.

Technik ändert sich schneller als Fachlichkeit

So bleibt es dabei: Große Teile von Legacy-Systemen werden einfach weiterbetrieben und totgepflegt. Es wird versucht, neue Systeme daran anzuknüpfen oder aufzusetzen und diese natürlich nach neuestem technologischen Stand zu bauen. Oft werden heute Microservices aus einem monolithischen Legacy-System herausgelöst , die fachlich abtrennbare Teile als eigenständige kleine Dienste bereitstellen und bei dieser oder späterer Gelegenheit intern neu entwickelt werden.

Das schafft man nicht mit dem gesamten System. Denn in diesem verbirgt sich eine ganze Menge Fachlichkeit, dessen Wahrheit einzig im Code enthalten ist. Die Fachlichkeit ist aktuell, und nur die Technik ist alt. Und nach einer Migration wird sie irgendwann wieder veraltet sein.

Bei Migrationen wird typischerweise versucht, die lückenhafte Dokumentation durch das ebenso lückenhaften Wissen der (ehemaligen) Beteiligten zu ergänzen und mit Erkenntnissen aus dem Code zu komplettieren. Mit dem Ergebnis werden die Anforderungen an das Neusystem erstellt und umgesetzt. Natürlich erfasst dieses Vorgehen die tatsächlichen Anforderungen nicht vollständig oder setzt sie nicht korrekt um. Außerdem werden in der Regel andere Personen das Neu-System umsetzen als das alte. Denn Letztere sind weiterhin mit der Pflege des Legacy-Systems ausgelastet, beherrschen die neue Zielarchitektur und den Technologie-Stack nicht ausreichend oder sind gar nicht mehr im Unternehmen.

Wie soll man dieses Dilemma lösen?

Legacy-System – Die Wahrheit liegt im Code

Um diese Brüche zu überwinden, sollte die einzige Quelle der Wahrheit verwendet werden: Der Programmcode. Dieser enthält zu jedem Zeitpunkt die absolute Wahrheit über das produktive Legacy-System. Warum wird diese Tatsache nicht ausgenutzt?

Ganz so einfach ist es natürlich nicht. Legacy-Systeme sind in anderen Programmiersprachen entwickelt, die Bedienmuster können sich deutlich unterscheiden (z.B. Host- im Vergleich zu Web-Anwendung), oder Teile der Programmlogik lassen sich nicht sinnvoll auf eine neue Zielarchitektur übertragen. Die Abstraktion des Programmcodes ist gleich null. Und Maschinen können nicht automatisch abstrahieren. Das ist, wie wenn man aus Schwein (Anforderungen) Wurst (Programm) herstellt und diesen Prozess umkehren möchte. Vertreter von Reverse-Engineering-Ansätzen mögen so etwas propagieren, doch sind mir noch keine Projekte begegnet, in denen das mit vertretbarem Aufwand hinreichend gut gelungen ist.

Dennoch lassen sich aus Programmcode und anderen Quellen des Systems Anteile der Wahrheit abstrahieren. Beispielsweise besitzt jedes maskenbasierte System eine Reihe von Masken, welche identifizierbar sind und deren Aufbau sich aus dem Code herauslesen lässt. Einige Konzepte sind wiederkehrend, beispielsweise die verwendeten UI-Widgets und ihre Anordnung (Layout) auf den Masken. Was für die Anwendungsmasken gilt, das lässt sich auch auf andere Strukturen der Anwendung übertragen: Domänenstrukturen, Abläufe, Entscheidungen.

Der schematische Anteil des Programmcodes ist in typischen Enterprise-Anwendungen recht hoch. Viel Programmcode dreht sich darum, welche Informationen den Nutzern präsentiert wird und wie diese im Backend verarbeitet (oft: persistiert) werden. Wenn allein diese Informationen extrahiert und automatisiert auf ein neues System übertragen werden könnten, dann wäre schon viel gewonnen.

Funktioniert eine teilautomatisierte Migration von Legacy-Systemen?

Tja, leider darf man hier keine Wunder erwarten. Jedes System ist unterschiedlich. Wie und welche Informationen aus Legacy-Systemen extrahiert werden können, ist immer anders. Auch die Zielarchitektur ist immer individuell.

Zum Beispiel könnte das Legacy-System eine Oracle-Forms -basierte Lösung sein, und das Ziel ein typischer moderner Stack mit Spring Boot im Backend und React am Frontend. Aus Oracle-Forms-Anwendungen lassen sich etwa Metadaten der Forms-Anwendung nach XML exportieren. Diese enthalten Informationen über Masken, Felder, Datenbindung, Validierung und jede Menge mehr. Mit den extrahierten Daten lassen sich dieselben Funktionen in einem anderen Technologie-Stack erstellen.

Für die Abbildung werden Transformationswerkzeuge benötigt, die in der Lage sind, die Quelldaten zu lesen und aufzubereiten und auf ein Zielsystem zu übertragen. Dies geschieht typischerweise durch Generierung von Quellcode für das Zielsystem. Diese Transformationswerkzeuge müssen individuell entwickelt werden.

Allerdings enthalten die Quelldaten auch jede Menge Informationen, die sehr spezifisch für das Legacy-System sind und sich schwer bis gar nicht übertragen lassen. Im Fall von Oracle Forms etwa wird das Verhalten von UI-Widgets durch PL/SQL Code beschrieben. Während vieles davon wiederum schematisch ist, sind bestimmte Prozeduren stark individuell. Dafür ist ein Parser zu implementieren, der den PL/SQL-Code erkennt und in (brauchbaren) Zielcode abbildet. Beides ist nicht trivial.

Lohnt sich die Entwicklung individueller Transformationswerkzeuge?

Das lässt sich pauschal nicht beantworten, außer mit: Kommt drauf an. Es muss zunächst untersucht werden, welche Informationen eines Legacy-Systems wie extrahiert werden können. Gibt es keine passenden Werkzeuge, dann müssen sie hergestellt werden. Wie aufwendig dies ist, hängt davon ab, welche Quellen wie abgerufen werden können. Der oben genannte XML-Export hat sich zum Beispiel gut bewährt. Manchmal bleibt nur der reine Quellcode, dann wird es umso aufwendiger.

Die extrahierten Daten müssen in ein maschinenlesbares Format übertragen werden, welches alle relevanten Informationen enthält, um das Gerüst einer Zielanwendung zu erzeugen. Dazu erstellt man typischerweise manuell eine Referenzimplementierung für die Zielarchitektur. Diese ist so strukturiert ist, dass sich die aus den extrahierten Strukturdaten gewonnenen Informationen auf schematischen Code übertragen lassen. Dieser schematische Code ist von demjenigen Code getrennt, der sich nicht aus den Quelldaten ableiten lässt. Man spricht hier vom Generation-Gap-Pattern.

Hat man dies erreicht, dann lassen sich daraus Generatorschablonen ableiten. Der Generator muss ebenfalls individuell programmiert werden. Hierzu eignen sich Programmiersprachen, welche direkt Code-Templates als Feature unterstützen, zum Beispiel Xtend.

Man kann nun nach und nach weitere Informationen aus der Altanwendung extrahieren und mittels Generierung in schematischen Codeüberführen. Oft gibt es aber eine Grenze, bis zu der diese automatische Transformation sinnvoll ist. Sobald der Aufwand zur Erstellung der Transformationswerkzeuge den für eine manuelle Übertragung und Absicherung der übertragenen Funktionen übersteigt, dann sollte man bei der manuellen Neuimplementierung dieser Anteile bleiben.

Es gibt aber immer Anteile, bei denen der Aufwand zur Erstellung der nötigen Transformationswerkzeuge geringer ist als der manuelle Neuimplementierungsaufwand. Zusätzlich stellt der transformationsbasierte Ansatz sicher, dass die durch den Codegenerator schematisch erstellten Anteile immer homogen implementiert sind und eine höhere Qualität als manueller Code aufweisen. Denn sollte in den Transformationsschablonen ein Fehler stecken, kann dieser zentral behoben werden und ist nach erneutem Generatorlauf an allen Stellen im Zielcode behoben.

Für wen ist die teilautomatisierte Migration von Legacy-Systemen geeignet?

Auch darauf gibt es keine pauschale Antwort. Wir haben mit Unternehmen unterschiedlicher Branchen gearbeitet und sie bei ihren Migrationsvorhaben unterstützt. Zugegeben, nicht jedes Legacy-System eignet sich für die Migration nach unserem Ansatz. Wir haben die Erfahrung gemacht, dass die Potentiale innerhalb weniger Workshoptage zusammen mit den Verantwortlichen des Legacy-Systems aufgedeckt werden können. Auf dieser Basis lässt sich anschließend entscheiden, ob sich die Umsetzung eines Proof-of-Concept-Prototyps lohnt. Anhand dessen kann dann beurteilt werden, ob der Weg konsequent weiter verfolgt wird oder nicht.

Stehst auch du vor der Herausforderung, ein für das Unternehmen wichtiges Legacy-System zu modernisieren? Hast du mit dem Thema bereits Erfahrungen gemacht? Schreib mir deine Erfahrung gerne in die Kommentare!

Kommentare