Legacy Code in Altsystemen

Legacy Code in Altsystemen ist ein Thema, welches jeden softwareentwickelnden Bereich irgendwann mit Wucht trifft und zu einem Problem werden kann. Es gibt jedoch erprobte Lösungsansätze, mit denen Entwickler Stück für Stück die Altlasten abtragen können.

„Legacy Code. The phrase strikes disgust in the hearts of programmers.“ – Robert Martin, im Vorwort von ‚Working Effectively with Legacy Code‘, Michael Feathers, 2003.

Das erwartet Sie im folgenden Blog-Beitrag:

Charakteristika von Legacy Code

Wenn man fragt „Was ist Legacy Code eigentlich?“, ist eine der häufigsten Antworten „von Vorgängern geerbter Code“ oder etwas spezifischer „alter Code, den keiner versteht, aber noch benutzt wird.“ Wenn man sich nun diese Sätze ansieht, dann stellt sich unweigerlich die Frage: ist das schlimm, wenn der ursprüngliche Autor weg ist? Wie alt ist alt? Und was ist, wenn der Code perfekt dokumentiert ist, aber komplex? Ist das auch Legacy Code?

Legacy Code taucht meistens im Rahmen von Software Maintenance oder Infrastrukturänderungen auf. Plötzlich muss der Monolith in die Cloud migrieren, der schnell in Python geschriebene Code muss mit einem Browser kommunizieren oder ein neues Feature soll dem Altsystem hinzugefügt werden.

Und dann steht man da und fragt sich: wie schlimm wird es?

Manchmal sehr schlimm.

Bei jedem Projekt sollten die ersten Warnlampen furios blinken, wenn folgende Dinge zutreffen:

  • Designverbesserungen sind nicht mehr möglich
  • Software Maintenance ist schwierig
  • Änderungen sind schwierig
 

Selbst Code, der erst eine Handvoll an Jahren alt ist, kann diesen Status erreichen. Es gibt also keine definitive Altersgrenze. Wenn die Warnlampen blinken, sollte man sich das Problem genauer anschauen. Wie schlimm ist es?

Legacy Code hat meist folgende Charakteristika:

  • Keine Tests
  • Veraltete Technologien
  • Fehlende ursprüngliche Autoren
  • Technische Schuld
 

Umso mehr Punkte zutreffen, desto weniger zukunftssicher kann die Software angesehen werden. Die Größe des Projektteams und der Codebasis kann hier getrost als exponentieller Faktor angesehen werden.

Die einzelnen Charakteristika wirken hierbei nun unterschiedlich und können auch jeweils mit anderen Lösungen behoben werden. Das werden wir uns im Folgenden ein wenig näher ansehen.

Keine Tests

Test Driven Development (TDD) ist zwar keine ganz neue Erfindung mehr, aber viele alte (oder sehr alte) Softwaresysteme wurden trotzdem (fast) ohne Tests geschrieben. Dies hat zur Folge, dass es häufig keine Möglichkeit gibt zu testen, ob einzelne Komponenten nach einer Änderung noch nach Spezifikation funktionieren. Falls nicht ein Qualitätsverlust in Kauf genommen wird, führt dies unweigerlich zu den ersten offenkundig negativen Auswirkungen:

Eskalierende Entwicklungs- und Testzeiten

Nach jeder Änderung, und sei es nur ein Sicherheitspatch im Rahmen der Software Maintenance, muss somit das gesamte System komplett getestet werden. Häufig passiert dies manuell, da die wenigen automatisierten Tests kaum das Nötigste abdecken. Dies verschlingt Zeit, die ein modernes, agiles Team meist nicht hat.

Testausbau

Die Lösung, einfach mehr Tests hinzuzufügen, ist jedoch ebenfalls sehr aufwendig und setzt meist ein grundlegendes Verständnis des Frameworks voraus. Somit kann es schwer fallen, mit sinnvollem Aufwand Unit Tests nachträglich hinzuzufügen. In solchen Fällen können häufig gute Integration- und Regressionstests den Job übernehmen, bis das Team ein grundlegenderes Verständnis erworben hat. Nur mit Tests kann verlässlich Qualität garantiert und der Legacy Code zukunftssicher gemacht werden.

Abhängigkeiten

Bei Legacy Code kann es von außen zu kaum erkennbaren, noch weniger vorhersehbaren Abhängigkeiten kommen, die schnell mal eine gutgemeinte Änderung unmöglich machen. Um diese Abhängigkeiten aufzubrechen, könnten „Nähte“ („seams“ im Englischen) innerhalb des Frameworks gesucht werden. Dies sind Orte, an denen das Verhalten der Software verändert werden kann, ohne die Software selbst verändern zu müssen. Diese wertvollen Chancen zu finden und als Hebel für Änderungen und selektive Tests zu nutzen, ist eine weitere Technik, um Stück für Stück den Legacy Code zu beherrschen.

Nachdem dies geschehen ist, können auch weit aggressivere Strategien wie das Wrappen von Teilen des Altsystems nach außen hin, um die Verwendbarkeit zu erhöhen, bis hin zum völligen Ersetzen bestimmter Abschnitte angewandt werden. Doch egal wie das Ziel aussieht, das Auflösen von Abhängigkeiten innerhalb des Codes sollte so früh wie möglich angestrebt werden, falls ein stressfreier Arbeitsalltag angestrebt wird.

Veraltete Technologien

Neu ist zwar nicht immer besser, wie jeder erfahrene Entwickler weiß, aber kaum einer würde die Softwareentwicklung für eine verschlafene Gemeinde ohne jeglichen Fortschritt halten. Häufig kennen die aktuellen Entwickler die alte Technologie gerade noch von der Universität oder aus Erzählungen und fragen sich nervös, ob es einschlägige YouTube Videos gibt. COBOL ist hier das bekannteste Beispiel und mit gutem Grund als Legacy Technology legendär.

Solche Wissensdefizite im Hinblick auf den Legacy Code führen direkt zum nächsten Problem:

Qualifizierte Entwickler für Alttechnologien

Meist findet man sie kaum im eigenen Unternehmen, was zu einem Lernprozess bei den vorhandenen Softwareentwicklern führt – und zwangsweise zu Fehlern während des Lernprozesses. Fehler, die sich auf das gesamte Projekt auswirken können. Hierbei ist die externe Suche nach Personen mit einschlägiger Erfahrung eine gute Möglichkeit, sich vor kostenintensiven Fehlern zu schützen.

Fehlende ursprüngliche Autoren

Bevor man sich jedoch der mühsamen externen Suche widmet, sollte man einen Blick auf die eigenen Mitarbeiter werfen und die ursprünglichen Wissensträger suchen. Vielleicht kann man sie noch kontaktieren? Fast immer lohnt sich ein Anruf. Je größer das Legacy System, desto wichtiger wird das alte Wissen der Entwickler und deren vergrabenes Wissen. Selbst ein paar Stunden können hier äußerst wertvoll und kosteneffizient sein, um das Projekt aufzusetzen und grundsätzlich zu verstehen. Häufig genug ist das jedoch aus verschiedenen Gründen nicht mehr möglich.

In dem Falle kann man nur hoffen, dass die anderen Faktoren möglichst wenig zutreffen.

Technische Schuld

Technische Schulden sind „shortcuts“, die während des Entwicklungsprozesses genommen wurden und Maintenance und Stabilität eines Systems nachhaltig negativ beeinflussen. Als Beispiel kann man hier das Framework nennen, welches mehrere Versionen hinter dem aktuellen Stand hängt oder die Architektur, welche für ein Minimally Viable Product (MVP) oder einen Prototyp konzipiert worden war, doch nie eine grundlegende Erneuerung erfahren hat.

Dass die Dokumentation fehlt, darf dann eher schon Erwartung als Überraschung sein.

Dokumentation

Wenige Softwareentwickler dokumentieren gerne, noch weniger dokumentieren gut und die wenigsten schaffen es mit hellseherischer Sicherheit zu dokumentieren, was ihr Nachfolger 15 Jahre später zum Verständnis braucht. Und wie viel Dokumentation oder Kommentare brauchen 10.000 Zeilen Code wirklich (Stichwort “Clean Code”)?

Wenn das Altsystem den Legacy Status erreicht hat, aber die Dokumentation fehlt, kann diese nur schwer wieder nachgeholt werden. Die Entwickler sollten, wenn sie etwas Neues verstanden haben, dies in einem geeigneten Rahmen niederschreiben, sei es als einfacher Kommentar im Code oder als Eintrag in einer modernen Wiki-Software wie Confluence. Bei völligen Unbekannten wie ehemalige teaminterne Fachbegriffe sollten diese gesammelt und nach und nach mit Definitionen gefüllt werden. Die neue Dokumentation muss Stück für Stück entstehen, um die Software zukunftssicher zu machen.

Verringerung der Schuld

Die Frage, in welche Themen wie und wieviel Energie und Zeit investiert werden, ist eine sehr individuelle und setzt viel Erfahrung bei allen Entscheidungsträgern voraus.

Wenn das Altsystem nur noch mit enormem Aufwand wartbar ist, die Software Migration technisch nicht möglich ist oder gar das Framework zu einem dauerhaften Sicherheitsrisiko wird, könnte es an der Zeit sein in den sauren Apfel zu beißen. Manchmal ist „rip and replace“ der schnellste und sauberste Weg, um die Schuld zu beseitigen. Dies bringt nach außen hin das Projekt kaum weiter, ermöglicht aber einen frischen Neustart mit den gewonnenen Erkenntnissen. Dieser Schritt sollte jedoch mit Bedacht gewählt werden, da er wieder neue Risiken birgt.

Fazit

Legacy Code ist etwas, dass uns in Zukunft immer häufiger begegnen wird. Bereits jetzt sind hinter vielen neuen Technologien und Oberflächen alte Frameworks und komplexe Monolithen im Einsatz, die leise ihren Dienst erfüllen – bis eben nichts mehr geht.

Um dem Legacy Code Desaster vorzubeugen, hilft nur

  • Dokumentieren
  • Erstellen von automatisierten Tests
  • Konsistentes Anlernen neuer Teammitglieder
  • Abbau von Technischer Schuld

Falls Sie mehr über das Thema wissen möchten, empfehle ich das Buch ‚Working Effectively with Legacy Code‘ von Michael Feathers (zu Deutsch: “Effektives Arbeiten mit Legacy Code”). Es ist bereits einige Jahre alt, aber noch immer aktuell. Ebenfalls empfehlenswert sind der Blog von Michael Feathers und der von Nicolas Carlo.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte beachte unsere Nutzungsrichtlinien

Mehr zu diesem Thema

Um unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Weitere Informationen finden Sie in unserer Datenschutzerklärung.