Technische Schulden, Legacy Code, Maintenance Load

geschrieben von Michael Hauck, Lesezeit 4 Minuten
Oft kämpfen Teams mit hohen technischen Aufwänden, um kontinuierlich und zeitnah neue Features zu releasen und die Software weiterentwickeln zu können. Um diese technischen Probleme und Aufwände zu verdeutlichen, werden häufig verschiedene Begriffe gebraucht, die sich jedoch in ihrer Bedeutung unterscheiden und oft auch unterschiedlich aufgefasst werden (manchmal sogar im selben Unternehmen).

In diesem Artikel gehen wir auf den Begriffswirrwarr ein und grenzen die Begriffe technische Schulden, Legacy Code und Maintenance Load voneinander ab.

Technische Schulden / Tech Debt

Dieser Begriff ist sicher dahingehend der problematischste, als dass er oft missverstanden wird und von manchen aus diesem Grund schon bewusst nicht mehr verwendet wird.

Ursprünglich geprägt wurde der Begriff von Ward Cunningham, als er an der Entwicklung einer Software im Finanzumfeld beteiligt war. Um seinem Vorgesetztem etwas zu verdeutlichen, griff er zu der Finanzmetapher: Wenn wir versuchen, Features möglichst schnell rauszubringen, anstatt alles vorab zu spezifizieren, kann es sein, dass wir nach der Auslieferung neue Erkenntnisse erlangen, die bei der ursprünglichen Entwicklung noch nicht bekannt waren. Wenn wir die Software nicht anpassen, sodass sie das neue Wissen widerspiegelt, werden wir durch diese Abweichung immer neue Zusatzaufwände haben - wie ein Zins, der auf Schulden gezahlt werden muss, die vorher aufgenommen wurden [1]. Wenn man diese Abweichung nie angeht, man also die Schulden nicht zurückzahlt, wird man immer mehr mit Zusatzaufwänden beschäftigt sein und die Produktivität wird runtergehen. Um bei der Metapher zu bleiben: Wenn ich immer neue Schulden anhäufe, habe ich irgendwann kein Geld mehr, um neue Ausgaben zu tätigen oder die Kredite zu tilgen, sondern bin nur mit Bezahlen der Zinsen für die Kredite beschäftigt.

Es gibt einige Erweiterungen des Begriffs, die dieser Metapher nicht widersprechen: Martin Fowler definiert technische Schulden unter anderem auch damit, dass bewusst oder unbewusst technische Schulden gemacht werden, weil gewisse technische Skills im Team fehlen oder weil bewusst auf eine "gründlichere" Lösung verzichtet wird, um schneller mit einem Feature live gehen zu können [2].

Beim Begriff des Aufnehmens von Schulden spielen also unterschiedliche Aspekte eine Rolle. Man kann den Aspekt der Kosten des Zurückzahlens betrachten (Kreditzins) oder die bewusste Entscheidung, Aufwände in die Zukunft zu verlagern. Was schlussendlich immer bleibt: Jede technische Schuld verursacht einen Zusatzaufwand a) weil sie existiert und die Software damit weiterentwickelt werden soll, und b) wenn sie abgebaut werden muss.

Was auf der anderen Seite genauso stimmt: Schlechter Code, der aus Faulheit oder Unwissenheit so geschrieben wird, ist per se erstmal nur schlechter Code. Erst wenn er wissentlich und bewusst so in Kauf genommen wird, wird er zur technischen Schuld. Meistens hat die Entstehung solcher Codebasen aber andere Gründe.

Unabhängig davon kann uns die Metapher nur dann helfen, wenn wir dadurch besser die Probleme in der Softwareentwicklung kommunizieren und angehen können. Manchen Stakeholdern hilft der Begriff bei der Verdeutlichung.

Legacy Code

Unter Legacy Code wird meistens Code verstanden, der nur noch mit hohen Aufwänden weiterentwickelt werden kann, z.B. weil er ohne Tests geschrieben wurde. Andere verstehen unter Legacy Code z.B. den Code eines angebunden Altsystems, der noch mit Codeprinzipien oder angebundenen Bibliotheken und Frameworks entwickelt wurde, die im entsprechenden Team als nicht mehr zeitgemäß angesehen werden.

Auch hier wird wieder folgendes klar: Zum Einen gibt es keine einheitliche Definition, was unter Legacy Code fällt und was nicht. Und zum Anderen ist die Bewertung, was Legacy Code genau umfasst, auch immer subjektiv. Die Bewertung, welche Art von Code als Legacy Code angesehen wird, fällt tendenziell in verschiedenen Teams unterschiedlich aus; nicht selten herrscht auch innerhalb von Teams nicht immer Einigkeit darüber, was als Legacy Code zu sehen ist und was nicht. Oft schaffen es Teams deshalb auch gar nicht, einen aus ihrer Sicht sinnvollen Umgang mit Altlasten zu erarbeiten.

Oftmals steckt hinter der Bewertung beider Begriffe, also technischer Schuld und Legacy Code, unbewusst in den Teams aber die gleiche Motivation: Wenn diese Stellen im System nachhaltig viel Aufwand verursachen und das System dennoch kurz- und langfristig weiterentwickelt werden soll, wie gehe ich dann mit diesen Stellen um? Wie bekomme ich meine Codequalität hier in den Griff und das am besten auch nachhaltig?

Bei der Bewertung im Umgang mit technischen Schulden und Legacy Code und der Entscheidungsfindung kann die Arbeit am Tech Debt Dilemma helfen.

Ein weitere wichtiger Punkt ist die Auseinandersetzung mit der Maintenance Load im Team.

Maintenance Load

Unabhängig davon, ob Teams von technischer Schuld, Legacy Code, Altlasten oder anderem sprechen, am Schluss dreht sich immer die Frage, wie mit Code umgegangen werden soll, der aus technischer Sicht als veraltet, suboptimal, oder schwer zu warten bewertet wird.

Eine grundlegende Frage, die man sich zuerst mal stellen sollte: Ist es realistisch, solchen Code jemals komplett abzubauen?

Wenn sich Teams mit dieser Frage auseinandersetzen, führt dies nämlich oft zu mehreren Erkenntnissen:

  • Mancher Code ist zwar qualitativ nicht gut, aber in einem Modul beheimatet, was man mittlerweile kaum noch anfassen oder verändern muss.
  • Ein kompletter Abbau von altem Code im gesamten System ist unrealistisch.
  • Manchen Code würde man zwar aus heutiger Sicht anders schreiben, aus damaliger Sicht war die Herangehensweise aber ok und das macht das System nun mal aus.
  • Wir können nicht vermeiden, dass wir aus heutiger Sicht Code zwar anders schreiben würden als damals, aber dennoch nicht davor gefeit sind, heute den neuen Legacy Code von morgen zu schreiben.

Welcher Code also wie modernisiert werden sollte, kann also nicht pauschal festgelegt werden. Stattdessen hat sich die Maintenance Load in den Teams als wichtiges Signal herausgestellt, das bei einer solchen Bewertung hilft:

Hierbei wird versucht, Entwicklungstätigkeiten, die nicht direkt auf die Entwicklung neuer Features eingehen, sichtbar zu machen. Dazu zählen dann auch genau der Umgang mit den obigen Themen, z.B. Umbauten von altem Code, Modernisierungen und Refactorings entsprechender Code-Stellen, Bugfixes aufgrund von fehlender Tests. Es kann aber auch sein, dass das Team deutlich mehr Zeit an anderen Stellen, wie z.B. eine instabilen CI/CD-Pipeline, investieren muss. Deswegen sollten die technischen Schulden/Legacy Code danach gewichtet betrachtet werden, wie sehr sie das Team in der alltäglichen Arbeit verlangsamen.

Hauptsächlich hilft eine solche Übung aber, die grundlegenden Maintenance-Aufwände erstmal sichtbar werden zu lassen. Erst danach sollten mögliche Verbesserungen gesammelt und diskutiert werden. Details hierzu haben wir in einem anderen Artikel beschrieben.

Referenzen

[1]: Ward Cunningham über die Debt-Metapher

[2]: Martin Fowler: Technical Debt Quadrant