In der täglichen Praxis sind wir ständig von Legacy-Software umgeben. Von wirklich alten Systemen genauso wie von unfreiwillig schnell gealterten Greenfield-Projekten. Unabhängig von der Vorgeschichte leiden Teams unter den Herausforderungen, mit denen Legacy-Software sie konfrontiert. Wie gelingt der Übergang von der Verzweiflung in einen konstruktiven Arbeitsmodus?
Der Alltag in der Softwareentwicklung konfrontiert uns fortlaufend mit Legacy-Software. Der Begriff bedeutet wortwörtlich eigentlich Erbe oder Vermächtnis. Die negative Assoziation in Softwareteams stammt aus der Belastung und den unangenehmen Begleitumständen, die solche Systeme in der täglichen Arbeit mit sich bringen. Wann ist Legacy also kritisch oder sogar gefährlich?
Stellen wir uns die folgende Situation vor, Ähnlichkeiten zu lebenden Personen oder realen Projekten sind tragisch, aber rein zufällig: Ein Softwaresystem hat eine sinnvolle Erweiterung erhalten. Leider sind die geänderten Daten jeweils nur nach einem Refresh der Seite sichtbar. Um nicht zu tief in die Logik einzugreifen, erscheint ein einfacher JavaScript Reload nach dem Speichern die sinnvollste Lösung zu sein. Eigentlich nur eine unverfängliche Zeile an der richtigen Stelle im HTML. Der Werkstudent, der die Anforderung umgesetzt hat, ist aktuell leider in der Prüfungsphase.
So versucht sich eine projektfremde Entwicklerin an der Einbindung des JavaScript Snippets. Zunächst muss sie lokal eine Virtual Machine starten, da das Produkt ein sehr spezielles Active Directory für die Authentifizierung verwendet, das sich nicht containerisieren lässt. Nach dem erfolgreichen Start und der Konfiguration der Virtual Machine startet sie den Ant Build. Dieser schlägt fehl, weil die lokal installierte Version inkompatibel mit dem Projekt ist. Also installiert sie die passende Version und nach etwas Kampf mit den Umgebungsvariablen läuft der Build erfolgreich durch!
Leider funktioniert allerdings der Refresh noch nicht wie gewünscht. Auch ein Shutdown der lokalen Tomcat-Instanz und ein Leeren des webapps-Verzeichnisses bringt nach erneutem Build nicht das gewünschte Verhalten. Die Verzweiflung wächst, aber auch ein Leeren des Browsercaches führt nicht zum Ergebnis. Zwei weitere explorative Build-Durchläufe machen klar, dass im Projekt sehr viel JavaScript Code dupliziert vorliegt und die Änderung in der falschen Datei eingebunden wurde. Mit alerts und console.log ist dann endlich die korrekte Stelle identifiziert und ein neuer Versuch startet …
Erfahrungen wie diese sind bei der Arbeit mit Legacy-Projekten in unterschiedlicher Ausprägung an der Tagesordnung. Daneben gibt es diverse, in die Jahre gekommene Projekte, in denen bei Wartung und Weiterentwicklung keine Probleme auftreten. Handlungsbedarf besteht spätestens, wenn bestimmte Symptome im Projektalltag gehäuft auftreten.
Ein kompliziertes lokales Set-up und Onboarding weiterer Entwickler:innen ist ein weiteres Symptom für kritische Legacy. Moderne Frameworks ermöglichen mit Grundsätzen wie Convention-over-Configuration die Lieferung sinnvoller Grundkonfigurationen für die lokale Entwicklung. Über Profile werden externe Schnittstellen oder Services gemockt, alles andere stellen lokal laufende Container zur Verfügung. Ähnliches gilt für Build-Tools: Diese starten die Software in der Grundkonfiguration und ermöglichen schnelle lokale Testläufe. Erstrebenswert ist das Fünf-Minuten-Set-up: Im Optimalfall vergehen zwischen Check-out aus dem Versionskontrollsystem und dem erfolgreichen lokalen Start nicht mehr als fünf Minuten. Legacy-Systeme erfordern oft einen deutlich höheren Einrichtungsaufwand.
Ein kompliziertes lokales Set-up ist vor allem für die Skalierung in schwierigen Projektphasen problematisch. Nicht selten kostet das Onboarding zusätzlicher Entwickler:innen einen ganzen Tag und bremst das Projektteam massiv aus. Anforderungen müssen bis zu einer harten Deadline umgesetzt werden, die Projektmitarbeitenden haben weder die Kapazitäten für die Umsetzung noch für das Onboarding weiterer Kräfte. Selbst wenn das Onboarding gelingt, macht manche Legacy-Architektur paralleles Arbeiten am Projekt schwierig bis unmöglich. Zum Beispiel verhindert die exklusive Verwendung gemeinsamer Ressourcen und Services parallele Testläufe mehrerer Projektbeteiligter.
Gut gepflegte Legacy-Software mit konsequent umgesetztem Design kann auch nach Jahren gut erweitert werden. In der Realität verhalten sich Legacy-Systeme jedoch häufig wie das beliebte Geschicklichkeitsspiel: Spielziel bei Mikado ist es, aus einem Haufen von Holzspießen einen einzelnen zu entfernen, ohne dass sich auch nur ein anderer Spieß bewegt. Analog ist das Spielziel bei Bugfixes und Erweiterungen in kritischer Legacy, genau eine Klasse oder Komponente anzufassen, ohne weiteres Fehlverhalten auszulösen. Um die von Kunden gerne zurückgemeldete „Verschlimmbesserung“ zu umgehen, greifen Entwickler:innen dann gerne zum Zwiebelschalen-Anti-Pattern: Unverständlicher oder kritischer Bestandscode wird in eine weitere Schicht gehüllt, um die eigentliche Logik nicht zu berühren. All das führt zu einer Stagnation der Wertschöpfung, da weder grundlegende Aufräumarbeiten noch sinnvolle Featureentwicklung stattfinden.
Insbesondere von Anwendern erneut gemeldete Fehler untergraben das Vertrauen in das Projektteam und verschärfen den Ton in der Projektkommunikation. Kritische Legacy ist anfällig für diese Fehlerregression. Die Gründe hierfür sind vielfältig: schlechte Codequalität, fehlende oder unzureichende automatisierte Tests bis hin zu unglücklichen Merging-Strategien.
Technische Schulden sind von Zeit zu Zeit unvermeidbar. Das Entscheidende ist ein sinnvoller Umgang mit ihnen. Das betrifft zum einen die Katalogisierung und zum anderen den geplanten Abbau. Findet keine Auflistung und vor allem kein Abbau statt, verschärfen sich die Probleme mit längerer Liegezeit potenziell. Plötzlich müssen sehr große Updateschritte gegangen werden, die ein höheres Risiko mit sich bringen.
Das Set-up moderner Softwaresysteme zielt auf schnelle Releasezyklen ab. Eine lange Cycle Time ist ein weiteres Symptom für kritische Legacy. Im Wesentlichen beschreibt sie die Zeit, die zwischen Arbeitsbeginn und Veröffentlichung eines Features liegt. Je länger sie durch ungünstige Prozesse, Schleifen, oder durch Bearbeitung von Seiteneffekten ist, desto schlechter ist die Wertschöpfung der Entwicklung.
Zuletzt steigen durch eine lange Cycle Time auch die Featurekosten in kritischer Legacy. Anforderungen, die in einem modernen Set-up schnell umsetzbar und damit wertschöpfend sind, verursachen in Legacy-Systemen unverhältnismäßig hohe Kosten.
Die oben geschilderten Symptome sind zum...