Technical Debt Dilemma

geschrieben von Jens Happe, Lesezeit 4 Minuten
Wenn ein Softwaresystem ein gewisses Alter erreicht hat, hört man immer wieder die Begriffe 'technische Schulden', 'Technical Debt' oder kurz 'TechDebt'. Es geht nicht mehr so schnell voran, wie man möchte und überall kommen höhere Aufwände zusammen als erwartet. Trotzdem muss die Featureentwicklung schnell weitergehen. Wie kommt man aus dieser Zwickmühle heraus? Unsere Gedanken zu dem Thema haben wir in diesem Artikel festgehalten.

Vielleicht kennst du folgenden Dialog oder sogar schon einmal selbst geführt:

Product Manager: "Wir müssen das jetzt so schnell wie möglich rausbringen. Wie schätzt du den Aufwand ein?"

Engineer: "Schwierig. Die Stelle ist inzwischen echt kompliziert. Da müssen wir erstmal aufräumen, mindestens [deutlich mehr Zeit als erwartet]."

Product Manager: "Bekommen wir das nicht einfacher hin?"

Engineer: "Das ginge schon irgendwie, aber wir haben schon so viele technische Schulden angehäuft..."

Die Diskussion kann beliebig lang weitergehen und in beliebig großen Runden geführt werden. Am Ende einigt man sich meist darauf, das Feature doch in irgendeiner, manchmal reduzierten, Form umzusetzen und die technischen Schulden erstmal technische Schulden sein zu lassen. Bis es dann zu viel wird und die Entscheidung fällt alles neu zu machen.

Dilemma

Die Diskussion dreht sich im Kern um folgenden Konflikt:

  • Um in Zukunft schnell neue Features liefern zu können, müssen wir technische Schulden abbauen.
  • Um jetzt schnell neue Features liefern zu können, dürfen wir keine technischen Schulden abbauen.

Um langfristig ein erfolgreiches Unternehmen aufzubauen, müssen wir jetzt und in Zukunft schnell neue Features liefern. Beide Perspektiven haben also ihre volle Berechtigung. Was dazu führt, dass man damit sowohl Technical Debt abbauen, als auch sie nicht abbauen muss.

Als Konsequenz schwanken Unternehmen oft zwischen beiden Extremen ('Wir räumen das später auf.' vs. 'Wir machen gerade einen Rewrite...'). Andere versuchen sich mit einer X%-Regel zu helfen (z.B. "20% unserer Zeit nutzen wir für den Abbau von Technical Debt."). Aber kann das wirklich die Lösung sein?

Annahmen

Hinter diesem Dilemma stehen unter anderem die Annahmen:

  • Wenn wir jetzt technische Schulden abbauen, dann ist die Codebasis oder Architektur danach deutlich besser.
  • Wir können entweder technische Schulden abbauen oder neue Features entwickeln.

Natürlich gibt es noch jede Menge weitere Annahmen, die man an dieser Stelle diskutieren könnte. Wir beschränken uns hier aber auf diese beiden und stellen sie auf die Probe.

Wenn wir jetzt technische Schulden abbauen, dann ist die Codebasis oder Architektur danach deutlich besser.

Wie oft ist nach einem größeren Umbau der Code oder die Architektur nicht besser oder sogar schlimmer geworden? Nur weil eine Person (oder eine kleine Gruppe) etwas als besser oder einfacher empfindet, heißt es nicht, dass andere das auch tun. Es bedeutet auch nicht zwangsläufig, dass die Features, die in Zukunft kommen, sich mit dem Umbau besser umsetzen lassen.

Das ist für die meisten Entwickler:innen (mich eingeschlossen) schwer zu akzeptieren: Ein Umbau verbessert nicht unbedingt die Codequalität.

Damit ein Umbau den gewünschten, positiven Effekt hat, muss es ein übergreifendes Verständnis von "besser" bzw. dem erwünschten Zielzustand geben. Solange es das nicht gibt, bleibt jeder Umbau ein Glücksspiel.

Wir können entweder technische Schulden abbauen oder neue Features entwickeln.

Anders formuliert: 'Der Abbau von Technical Debt und Featureentwicklung schließen sich gegenseitig aus.' Um diese Annahme zu hinterfragen, können wir Martin Fowler zitieren [1]:

"When I need to add a new feature to a codebase, I look at the existing code and consider whether it's structured in such a way to make the new change straightforward. If it isn't, then I refactor the existing code to make this new addition easy. By refactoring first in this way, I usually find it's faster than if I hadn't carried out the refactoring first."

Refactorings sind kleine verhaltensbewahrende Transformationen [2] und als solche Teil der täglichen Arbeit. Richtig ausgeführt erhöhen sie den Aufwand in der Featureentwicklung nicht, ganz im Gegenteil. Sie werden aber auch nicht losgelöst von der Featureentwicklung durchgeführt, sondern sind Bestandteil von ihr.

Lösung

Daraus ergeben sich zwei zentrale Punkte zur Auflösung des Dilemmas:

  1. Wir benötigen ein gemeinsames Verständnis vom Zielzustand für alle Entwickler:innen
  2. Wir benötigen ein gutes Vorgehen für kontinuierliches Refactoring im Rahmen der Featureentwicklung.

Diese Ziele kann man mit der Einführung von Blueprints und der aktiv gelebten Scout Rule erreichen.

Wir definieren Blueprints als inhouse Design Patterns und grundsätzliche Architekturentscheidungen, die beschreiben, wie immer wiederkehrende Probleme in der Softwareentwicklung innerhalb des Unternehmens (der Abteilung, des Teams - welcher Rahmen hier auch immer sinnvoll ist) gelöst werden. Sie können z.B. vorgeben, wie APIs im Backend umgesetzt werden. Das umfasst einfache Dinge, wie die Struktur der URL und das Naming, aber auch grundsätzliche Architekturentscheidungen, zu den Modellen für die API und deren Konstruktion und der Aufteilung der Logik.

Die Scout Rule besagt im Wesentlichen: "Wann immer du ein Stück Code anfasst, hinterlasse es in einem besseren Zustand als du es vorgefunden hast." Durch die Einführung der Blueprints gibt es jetzt Einigkeit darüber, was genau "besser" bedeutet. Jede Entwickler:in kann den Code, den sie anfasst, in die entsprechende Richtung verbessern.

Dieser Vorschlag ist eine mögliche Lösung des Dilemmas, die wir bereits erfolgreich eingesetzt haben. Sie wird nicht in jedem Fall passend sein. Durch das Formulieren und Hinterfragen weiterer Annahmen, die im aktuellen Kontext relevant sind, lassen sich andere Ansätze herleiten.

Fazit

Mit der Kombination aus Blueprints und der Scout Rule können Refactorings im Rahmen der normalen Featureentwicklung ausgeführt werden, ohne die Entwicklung selbst auszubremsen.

Klingt erstmal ziemlich einfach und auch nicht wirklich neu. Im ersten Moment haben wir das auch gedacht. Es ist allerdings alles andere als einfach gute und nützlich Blueprints zu erstellen, bei denen alle Entwickler:innen mitziehen. Auch die Blueprints im Alltag zu nutzen und zu verbessern ist eine große Herausforderung. Das Einhalten der Scout Rule ist alles andere als selbstverständlich. Es gibt so viele Freiheitsgrade und Unwägbarkeiten, die immer wieder zu Rückfragen und Diskussionen führen.

Also: Es ist nicht so leicht wie es im ersten Moment klingt, bringt aber tatsächlich schrittweise Klarheit und Verbesserungen mit sich. Die Entwicklung kommt in eine positive Aufwärtsspirale, in der die Codebasis sich mit der Zeit immer mehr dem Zielzustand annähert, statt sich immer weiter von ihm zu entfernen.

[1]: Refactoring Home Page

[2]: RefactoringMalapropism