Umgang mit Technical Debt
Tags: softwareentwicklung
Kategorie Software Engineering | 2 Kommentare »
Ich hätte schwören können, über den Umgang mit technischen Schulden (Technical Debt) habe ich schon mal was geschrieben, aber ich konnte nichts finden. Deshalb hier meine Erfahrungen der letzten 2 Jahrzehnte zum Thema Technical Debt in der Software-Entwicklung :-)
Definition Technische Schulden
Der Begriff Technical Debt geht auf Ward Cunningham zurück, der diesen Anfang der 1990 Jahre geprägt hat. Da Ward Cunningham in der Finanzbranche arbeitete, wählte er eine Metapher aus diesem Bereich. In Anlehnung an Ward Cunning hier meine saloppe Definition:
Technical Debt ist der Zins auf die großen und kleinen Schweinereien in unserem Code.
Wir können den Zins durch Abbau der technischen Schulden verringern. Tun wir dies nicht, wird es Dank des Zinseszins-Effekts immer schlimmer und irgendwann entwickeln wir fast nichts Neues mehr, da wir nur noch mit dem Schuldendienst beschäftigt sind.
Quellen von technischen Schulden
Jede noch so clever entwickelte Software altert. Die verwendeten Bibliotheken und Laufzeitumgebungen entwickeln sich ständig weiter, neue oder geänderte Anforderungen an die Software entstehen. Dies ist die Hauptquelle von technischen Schulden.
Technische Schulden entstehen bewusst. Oft wird gerade in stressigen Projektphasen die eine oder andere Abkürzung genommen, um den Termin doch noch zu schaffen. Wie in der Finanzbranche sind (technische) Schulden nicht per se schlecht, wenn sie aktiv gemanagt werden.
Technische Schulden entstehen durch mangelndes Wissen. Entwickler sind auch nur Menschen und lernen stetig hinzu. Was vor einem Jahr noch eine gute Idee war, mag mit neuer Erfahrung nun anders erscheinen.
Strategien zur Bewältigung von technischen Schulden
Bei einem Rohrbruch in der eigenen Wohnung dreht man so schnell als möglich das Wasser ab, bevor man mit dem Aufwischen beginnt. Entsprechend: Bevor man bestehende technische Schulden behebt, sorgt man dafür, dass nicht ständig neue Schulden angehäuft werden.
Nun beginnt man mit dem Aufwischen. Dabei konzentriert man sich auf die großen Pfützen, deren Beseitigung das beste Kosten-Nutzen-Verhältnis hat. Manche technische Schulden behebt man auch nie. Sie trocknen zwar nicht von selbst wie Wasserpfützen, aber man kann damit leben.
Schlussendlich gilt noch die Pfadfinder-Regel der Software-Entwicklung: Stolpert man in seiner täglichen Arbeit über eine Unsauberkeit im Code wie zum Beispiel schlecht geschnittene Funktionen oder unzureichende Tests, dann behebt man das gleich mit und verlässt den Code etwas sauberer als man ihn vorgefunden hat.
Technische Schulden vermeiden
Doch was kann man nun konkret tun, um technische Schulden zu vermeiden? Tatsächlich verfügen wir heute über eine Vielzahl an Werkzeugen. Die Basis bildet
- eine konsequente Testautomatisierung und
- vollständig automatisierte Bau- und Deployment Prozesse.
Dadurch kann ich sicherstellen, dass meine Software nach jeder Änderung weiterhin die Geschäftsanforderungen erfüllt und selbst kleine Änderungen nicht viel Aufwand benötigen.
Folgende Werkzeuge helfen beim Schreiben von sauberem Code:
- statische Code Analyse
- automatische Code Formatierung
- gemeinsame Coding Conventions der Entwickler
- AI Code Assistenten in der Entwicklungsumgebung
Heute ist es üblich, dass Code durch einen anderen Entwickler mittels Pull Request geprüft wird. Doch bevor ein anderer Mensch über den Code schaut, sollte dies ein AI Coding Assistent wie GitHub Co-Pilot tun. Diese Tools sind heute schon so gut, dass sie typische Fehler wie falsch benannte Variablen oder falsch genutzte Framework Funktionen erkennen. Anschließend kann sich der menschliche Reviewer viel besser auf das Software Design und die korrekte Umsetzung der fachlichen Anforderungen in seinem Review konzentrieren.
Aus meiner Erfahrung ist es auch sehr wichtig eine gute Mischung aus Erfahrenen und Berufseinsteigern im Team zu haben. Das Aufsetzen von Testautomatisierung, statischer Code Analyse und Coding Conventions muss nicht in jedem Projekt neu erfunden werden. Hier kann man stark von der Erfahrung profitieren. Auf der anderen Seite können neue Mitarbeiter wichtige Denkanstöße mitbringen, etwa neue Tools wie AI Code Assistenten.
Um ein Altern der Software zu verhindern, sollte man auch ein Werkzeug zum automatischen Aktualisieren von Abhängigkeiten wie Renovate oder Dependabot einsetzen. Solch ein Werkzeug prüft täglich, ob es eine neue Version gibt und führt ein Upgrade durch. Dank der Testautomatisierung weiß ich, ob alles mit der neuen Version weiterhin läuft und die Änderung wird automatisch in Produktion ausgerollt.
Zukünftig werden weitere Werkzeuge beim Aktualisieren von Code helfen. Schon heute migriert Angular den Projekt-Code bei jedem Release, wenn sich im Angular Framework etwas geändert hat. Mit OpenRewrite gibt es ein unabhängiges Framework, um Code automatisiert zu migrieren. Entsprechende OpenRewrite Rezepte werden jetzt immer häufiger direkt in Frameworks wie Quarkus integriert.
Umgang mit bestehenden technischen Schulden
Um bestehende technische Schulden abzuarbeiten, muss ich sie zunächst sichtbar machen. Bewährt hat sich folgendes Vorgehen:
- stichpunktartige Auflistung der technischen Schulden durch die Entwickler
- grobe Schätzung des Aufwands
- Priorisierung der Liste
- Detaillierung der obersten 3-5 technischen Schulden mit den Schritten zur Behebung
Durch das Aufschreiben und die Schätzung als Technical Debt Stories werden technische Schulden sicht- und messbar. Es entsteht ein Backlog von konkreten Schritten, um diese abzuarbeiten.
Anschließend diskutiert man diesen Backlog mit dem Produktmanager beziehungsweise der Person, die die Entwicklungsprioritäten festlegt. Manchmal muss man den Produktmanager zunächst über das Wesen von technischen Schulden aufklären. Da könnte dieser Blogpost als Leselektüre helfen…
Als Entwickler muss man sich aber auch in die Position des Produktmanagers reinversetzen: Die Bearbeitung von technischen Schulden bietet zunächst keinerlei geschäftlichen Nutzen. Die Software funktioniert wie zuvor, es gibt für die Nutzer keinen Mehrwert. Die Bearbeitung von technischen Schulden ist eine Investition in die Wartbarkeit der Software.
Bewährt hat sich die Einplanung von 1 oder 2 Technical Debt Stories in jedem Sprint bzw. Entwicklungsiteration. Manche Produktmanager übernehmen die Technical Debt Stories direkt in ihren Backlog. Idealerweise vereinbart mit dem Produktmanager ein festes Budget für die Abarbeitung von technischen Schulden, etwa 15% der Kapazität des Entwicklungsteams.
Je nach Zustand des Codes und ob bereits Maßnahmen zur Vermeidung von technischen Schulden ergriffen wurden, kann das erforderliche Budget stark variieren. Bei einer Neuentwicklung in einem vergangenen Projekt war fast gar kein Budget notwendig, da ich von Anfang alle Maßnahmen zur Vermeidung von technischen Schulden implementiert habe. In einem anderen Projekt hingegen habe ich mich über mehrere Wochen damit gekämpft, den Code auf aktuelle Versionen der verwendeten Frameworks und Laufzeitumgebung zu aktualisieren, nachdem sich niemand über Jahre darum gekümmert hatte und inzwischen riesige Sicherheitslücken in der Software klafften.
Fazit
Technische Schulden behebt man nicht nebenbei. Ein systematischer Ansatz vermeidet durch den Einsatz zahlreicher Werkzeuge das Entstehen von neuen technischen Schulden und sorgt für eine Abarbeitung bestehender technischer Schulden. Die Umsetzung ist nicht trivial, aber entlastet das Entwicklungsbudget messbar und gibt Entwicklern mehr Zeit für Neuentwicklungen. Es lohnt sich – versprochen!
Arbeitet der GitHub Co-Pilot denn lokal, so dass ich sicher sein kann, dass mein Code nicht abfließt oder muss man GitHub/Microsoft da einfach vertrauen, dass die den Code nicht abfischen?
Nein, der arbeitet nicht lokal. Als Administrator kannst du aber zumindest eine Checkbox setzen, dass dein Code und deine Prompts nicht für die Verbesserung genutzt werden dürfen. Als große Organisation mit vielen Entwicklern würde ich auch versuchen, hier ein Abkommen mit entsprechenden Schadenssummen zu formulieren.
Der Code Assistant von IntelliJ bietet die Möglichkeit ein lokales LLM einzubinden. Die Konfiguration ist aber nicht trivial und vermutlich verpasst man viele Neuentwicklungen. Auch bräuchte dann jeder Entwicklerrechner eine potente Grafikkarte.