Über unsMediaKontaktImpressum
Konstantin Sokolov & Egon Wuchner 18. Juli 2023

Technische Schulden

Wie sich im ersten Teil des Artikels gezeigt hat, ist der Begriff "Technische Schulden" nun mal in der Welt und schwer durch alternative Begriffe zu ersetzen, die sich dann auch durchsetzen würden. Zumal wir gesehen haben, dass die ideale Sprechweise und Handhabung schwer umsetzbar ist. Daher werden wir den Begriff hier weiter in Anführungszeichen verwenden, um bewusst zu machen, dass der Vergleich zu den finanziellen Schulden hinkt. Trotz der nun mal verwirrenden Natur des Begriffs wollen wir alternative, bessere Wege aufzeigen, wie man dem Management die Bedeutung und Dringlichkeit von "Technischen Schulden" vermitteln kann. Unser Ziel ist es, Sie von einem geringfügigen, aber regelmäßigen "Technische-Schulden-Budget" zu überzeugen.

Die Alternative: Bilder, KPIs und Root-Cause-Analyse

Als Erstes helfen Analogien weiter, die nicht technisch, sondern bildhaft sind und die jeder nachvollziehen kann. Beispiel: "Technische Schulden" sind "das ganze Chaos, das man in der Küche hinterlässt, wenn man denkt, dass man mit dem Kochen fertig ist".

Stellen Sie sich vor, Sie haben Gäste zum Essen eingeladen. Das Menü ist köstlich, das Essen schmeckt wunderbar und Sie genießen die Getränke. Aber es gibt eine Regel: Sie dürfen nicht in die Küche sehen! Die Küche steht für die Entwicklung der Software-Features, die dem Kunden geliefert wurden. Es könnte sein, dass dort Chaos herrscht, weil die Lieferung schnell gehen musste. Die Küche muss aufgeräumt werden, das Geschirr gespült werden und so weiter. Je unordentlicher die Küche ist – s. Titelbild –, desto schwieriger wird es sein, das nächste fantastische Essen zuzubereiten.

Bilder alleine reichen aber nicht aus. Sie stoßen zwar eher eine Veränderung an als technische Worte, aber sie dienen nicht als Entscheidungsgrundlage. Was zusätzlich nicht fehlen darf, sind harte Fakten. Das bedeutet, dass wir "Technische Schulden" irgendwie monitoren müssen. Dazu hilft es, sich klarzumachen, dass es zunächst Symptome von "Technischen Schulden" gibt, die es zu beobachten und zu messen gilt.

Links unten in Abb. 6 wird auf eines der Symptome Bezug genommen, von denen am Anfang die Rede war: die nicht-produktiven Kosten. Das sind die nicht-produktiven Aufwände, wie die normale Wartung in Form von Bugfixing oder das darüber hinausgehende Firefighting wegen "Technischer Schulden", also Aufwände, die nicht in die Feature-Entwicklung fließen. Zusätzlich gibt es auch die Zinsrate der "Technischen Schulden", die anfällt. Das äußert sich darin, dass das Hinzufügen neuer Features immer schwieriger wird und ihre Umsetzung mehr Zeit benötigt. Entweder sind es höhere Aufwände wegen Workarounds oder es fallen mehr implizite "Umbauarbeiten" am Code an, um ein Feature umzusetzen. Diese Symptome müssen messbar sein, um mit ersten harten Fakten aufs Management zugehen zu können.

Wie könnte die Messung und das Monitoring solcher Symptome aussehen? Wir werden nun darauf eingehen, wie das umgesetzt werden kann. Dabei nutzen wir viele Datenquellen, die es ohnehin – wie in Abb. 7 dargelegt – im Software-Entwicklungsbereich gibt. Es gibt zum einen das Code-Repository, dessen Nutzung zum Standard jeder Entwicklung gehört. Zudem wird normalerweise auch ein Issue-Tracker oder ein Requirements-Engineering-Tool verwendet. Im Issue-Tracker lassen sich nicht nur Bugs, sondern auch die funktionalen Tickets, wie z. B. User Stories oder Features, aufnehmen. Weiterhin gibt es z. B. Test-Tools (die nicht in Abb. 7 aufgenommen sind), um auf Testdaten (wie z. B. Testcoverage-Zahlen) zuzugreifen. Oder auch DevOp-Tools, um festzustellen, welcher Code reviewed wurde.

Und falls auch der Pfeil in der MItte in Abb.7 ("Commits+Issues+Time") zutrifft, d. h. dass die Assoziierung der Commits mit den Issues gegeben ist, lässt sich eine Analyse über die Zeit durchführen. Sprich, die gesamte Historie des Code-Repositorys von Commit zu Commit analysieren, um auf Kenngrößen zu kommen, die wir oben aufgeführt haben, sei es der Wartungsaufwand oder die Zinsrate der "Technischen Schulden".

Dabei sei erwähnt, dass der Pfeil in der Mitte, also die Verbindung von Commits mit Issues, über verschiedene Vorgehensweisen im Projekt realisiert werden kann:

  • Issue IDs in den Commit Messages
  • Feature-Branches mit der Issue ID als Teil des Branch-Namens
  • Pull Requests, die wiederum mit Issues verbunden sind

Dadurch lassen sich auf pragmatische Art und Weise, wie in Abb. 8 dargestellt, Entwicklungsaufwände wie der Wartungsaufwand oder Aufwände, die in die Feature-Entwicklung eingehen, messen. Über Commits hinweg gibt es hinzugefügte, modifizierte und gelöschte Codezeilen, die man zusammenzählt. Zusätzlich werden modifizierte Zeilen zweifach gewichtet, um der Tatsache Rechnung zu tragen, dass eine geänderte Zeile zunächst verstanden werden muss. Entsprechend muss man auch eine gelöschte Zeile verstanden haben, um zu wissen, was man da entfernt. Das ist kognitiver Aufwand, der aufgebracht wird, weswegen wir die Gewichtung bei modifizierten Zeilen vornehmen und auch gelöschte Zeilen bei der Aufwandsmessung berücksichtigen.

Der Aufwand lässt sich auf diese Art und Weise für das ganze System und für einzelne Files oder Verzeichnisse ermitteln. Mit der zusätzlichen Verbindung zu Issues lässt sich zudem der Aufwand pro Bug oder über alle Bugs hinweg und wiederum dieser Aufwand fürs ganze System, Files oder Verzeichnisse erfassen. Das Gleiche gilt für den Aufwand für einzelne User Stories bzw. Feature-Issues und allgemein für den gesamten Aufwand der Feature-Entwicklung.

Der Ansatz hat natürlich Einschränkungen, insbesondere im Falle von Edge Cases. Jeder hat schon mal einen halben oder ganzen Tag auf Stackoverflow oder beim Debuggen verbracht, um am Ende einen Fix mit vielleicht nur drei Zeilen Code zu produzieren. Das ist aber nicht die Regel. Im Durchschnitt – und das entspricht auch unserer Erfahrung – ist der zeilenbasierte Aufwand proportional zum zeitlichen. Ähnliches gilt auch für die Feature-Entwicklung. Es kann passieren, dass zur Umsetzung von Features viel Design-Aufwand erbracht wird, um sich eine Lösung zu überlegen, was getrennt von der Implementierung zu sehen ist. Man kann auch hier eventuell aus Erfahrungswerten des Projekts einen Faktor für den Aufwand dieser Features verwenden. Wenn man zum Beispiel weiß, dass 20 Prozent des Aufwandes pro Sprint für Design-Aufgaben anfallen, ergibt sich sogar ein genereller Faktor.

Ein anderes Beispiel sind automatisierte Änderungen wie z. B. Formatierungen oder das Hinzufügen eines Copyright- oder License-Headers. Das verursacht viele Zeilenänderungen im Code mit geringem zeitlichen Aufwand, so dass es gilt diese herauszufiltern. Mit diesen Anpassungsmöglichkeiten ist der Ansatz, den Aufwand zeilenbasiert und gewichtet zu messen – angesichts des Mangels an guten Alternativen – ein guter pragmatischer Weg. Denn Time-Tracking ist bei Entwicklern sehr unbeliebt und wird deswegen häufig auch nicht konsistent angewendet, so dass sich daraus keine belastbaren Zahlen ergeben.

Die Liste zählt zusammenfassend signifikante KPIs auf, die Teil der Analysen sind, um die Symptome der "Technischen Schulden" zu erfassen.

Key Performance Indicators für Symptome Technischer Schulden

  • Bugs/Features per Iteration/Release
  • Maintenance Effort/Feature Effort (non-productive vs. productive work)
  • Maintenance/Feature Effort Share (% of Development Effort)
  • Feature Effort Effectiveness (interest rate)
  • Extent of Follow-up Bugs (thus first pass not right)

Die Anzahl der Bugs/Features pro Iteration oder Release kann man natürlich auch über den Issue Tracker erhalten. Unserer Erfahrung nach ist diese Metrik aber wenig aussagekräftig. Denn oftmals greift die Pareto-Regel: 70-80 Prozent des Aufwands zur Entwicklung der Features fließt in 20-30 Prozent der Features ein. Der "Maintenance Effort/Feature Effort" dagegen spiegelt, wie gerade vorgestellt, den Wartungs- bzw. Feature-Entwicklungsaufwand wider. "Maintenance/Feature Effort Share" erfasst den jeweiligen nicht-produktiven bzw. produktiven prozentualen Anteil am gesamten Entwicklungsaufwand.

Mit "Feature Effort Effectiveness" wird die Zinsrate der Feature-Entwicklung gemessen. Aus Platzgründen gehen wir hier nicht weiter darauf ein. Von der Idee her wird damit gemessen, ob hauptsächlich neuer Code hinzugefügt wird (was man bei Features erwarten würde), oder ob implizit Umstrukturierungen und Refactorings bei der Implementierung der neuen Features vorgenommen werden. Eine weitere Metrik ist der Umfang von Follow-up-Bugs, d. h. der expliziten Feststellung, ob ein Bugfix an einer bestimmten Stelle ausgereicht hat, um ein Problem zu fixen, oder ob mehrere Bugfixes dazu nötig waren.

Mit der Messung der Symptome und der Manifestation in Zahlen folgt die nächste Phase, in der es darum geht, die "Technischen Schulden", wie in Abb. 9 skizziert, auch fokussiert und effektiv zu reduzieren. Ein erster Fokus besteht darin, die Hotspots im Code zu finden, die für schlechte KPI-Werte verantwortlich sind, insbesondere, wenn schon abzuschätzen ist, in welchen Bereichen des Codes in Zukunft neue Features umzusetzen wären.

Der zweite Fokus schafft eine Verbindung zum "Outcome", den Martin Fowler ins Spiel gebracht hat. Durch die Verbindungen von Features zu Code-Änderungen lassen sich auch die KPIs der "Technischen Schulden" im dazugehörigen Code zu diesen Features in Relation setzen. Wenn es sich um wichtige, vom Kunden stark genutzte Features handelt, sind Erweiterungen dazu zu erwarten. Daher lohnt es sich, vor allem die KPIs dieser Features und der "Technischen Schulden" dahinter zu beobachten und gegebenenfalls zu beheben.

Das dritte Kriterium, um die Reduktion der "Technischen Schulden" zu priorisieren, besteht darin, den benötigten Behebungsaufwand zu schätzen. Diese Schätzung kann beispielsweise manuell erfolgen. Alternativ bietet sich dazu ein automatisierter Ansatz an, so dass der Behebungsaufwand dem aktuellen Wartungsaufwand gegenübergestellt werden kann. Falls der Wartungsaufwand eines Hotspots relativ niedrig im Vergleich zum Aufwand der Behebung ausfällt, kann man es sich erlauben, über das "Wann" der Behebung zu entscheiden, während die ersten beiden Punkte in Abb. 9 sich auf das "Wo" der Behebung beziehen.

Womit wir beim letzten Gesichtspunkt angelangt sind, dem "Was muss ich tun und wie, um Technische Schulden zu beseitigen". Dazu müssen wir die Problem-Ursachen, die Root Causes, der "Technischen Schulden" kennen.

Abb. 10 zeigt wiederum beispielhaft, wie wir dabei vorgehen. Die Bubbles stellen Sourcecode-Verzeichnisse dar. Die Bubble-Größe entspricht dem Wartungsaufwand. Auf den Achsen sind zwei Code-Qualitätsmetriken zu sehen, die Code-Komplexität auf der x-Achse und die Anzahl der duplizierten Code-Zeilen auf der y-Achse. Der höhere Wartungsaufwand des braun gezeichneten Verzeichnisses scheint stark mit der hohen Code-Komplexität zusammenzuhängen, während der hohe Wartungsaufwand des orangen Verzeichnisses auf eine Mischung von beiden Code-Qualitäten zurückzuführen ist. Das Prinzip wird verallgemeinert, so dass die Symptome der "Technischen Schulden" und deren KPI-Werte (wie z. B. die Wartungsaufwände) mit den potentiellen Problem-Ursachen (Code-Qualität, Architektur-Qualität und anderen Ursachen) korreliert werden. Folgende Liste zeigt die Root Causes, die wir bestimmen und in Beziehung zu den KPIs der Symptome setzen.

Potential Root Causes der "Technischen Schulden"

  • Bad Code Quality
  • Bad Architecture Quality
  • Documentation Gaps
  • Effort/Knowledge Asymmetries
  • Insufficient Code Reviews
  • Sub-optimal Testing
  • Inefficient Engineering Processes

IT-Tage 2023

Konstantin Sokolov & Egon Wuchner halten zu einem ähnlichen Thema einen Vortrag auf unseren IT-Tagen im Dezember in Frankfurt.

Refactoring – Unabhängig von Microservices
(12.12.2023, 13:00 Uhr)

Zum einen stellt schlechte Code-Qualität eine mögliche Ursache dar. Dabei werden Code-Metriken bestehender Tools wie SonarQube integriert, statt neue zu erfinden und es werden eigene Architektur-Qualitätsmetriken angeboten, auf die weiter unten noch eingegangen wird. Es kann sich aber auch um andere Problem-Ursachen handeln, die zu schlechten KPIs führen: Dokumentationslücken oder schlechte Team-Kollaboration [1]. Alternativ oder zusätzlich kommen auch mangelnde Code-Reviews oder nicht ausreichendes Testing in Frage. Oder es handelt sich um unzureichende technische Entwicklungsprozesse. Zum Beispiel, falls nicht zu jedem Bugfix ein Test hinzugefügt wird oder nicht genügend Integrations- und Deployment-Aktivitäten automatisiert sind.

Gehen wir nun noch etwas genauer auf die Architektur-Qualitätsmetriken ein. Um die Verbindung zum "Outcome" herzustellen, helfen feature-basierte Architektur-Metriken. Wir heben das Single-Responsibility-Prinzip (s. Abb. 11) auf eine höhere Architekturstufe und führen die Konzepte der Feature-Cohesion und des Feature-Coupling als Modularitätsmetriken ein.

Die Begriffe Cohesion und Coupling kennen wir in der Software-Entwicklung nur zu gut. Was ist nun aber mit Feature-Cohesion gemeint? Links in Abb. 11 handelt es sich um eine Datei X, die zu zwei Features beiträgt, da sie Code enthält, der die Implementierung der beiden Features darstellt. Im Folgenden sind mit dem Begriff "Feature" immer funktionale Issues gemeint. Man kann nun leicht Folgendes erkennen: Zu je mehr Features Datei X beiträgt, desto wahrscheinlicher und umfangreicher werden potenzielle Seiteneffekte und unbeabsichtigte Fehler, die auftreten können. Wenn Feature A erweitert und neuer Code zur Datei X dafür hinzugefügt wird, muss darauf geachtet werden, dass die vorhandene Umsetzung der Features A und B nicht "gebrochen" wird.

Diese Betrachtungsweise lässt sich nicht nur auf einzelne Dateien anwenden, sondern auch auf Quellcode-Verzeichnisse oder auch auf einzelne Features. Ist eine Feature-Implementierung über die ganze Codebasis verstreut oder konnte diese recht lokalisiert und kohäsiv in nur wenigen Teilen der Codebasis umgesetzt werden.

Wenn wir als nächstes nicht nur eine Datei, sondern mehrere betrachten, wird das Bild wie in Abb. 11 rechts etwas komplizierter. Jetzt hat nicht nur Datei 3 zu den Features A und B beigetragen, sondern auch Datei 4. Wenn nun Feature A oder etwas in Datei 3 verändert wird, muss man nicht nur darauf Acht geben, nichts von Feature B zu brechen, sondern dass eventuell auch Änderungen in Datei 4 anfallen, damit die Implementierung konsistent bleibt bzw. vollständig wird. Je chaotischer dieser Feature-Überlapp über die Dateien hinweg ist, desto höher gekoppelt sind diese Dateien untereinander bzgl. dieser Features. Wenn man sich nun vorstellt, dass Feature A ein wichtiges Feature darstellt, das gut beim Kunden ankommt, möchte man den Code frei von Bugs, zuverlässig und entkoppelt von weiteren Features vorfinden, um leicht Erweiterungen umsetzen zu können. Dabei ist zu berücksichtigen, dass verwandte Features durchaus ein gekoppeltes lokales Feature-Cluster bilden dürfen. Der wesentliche Punkt besteht darin, dass nicht-verwandte Features (bzw. Feature-Cluster) voneinander entkoppelt sind [2].

Kommen wir nun zum Schluss. Was jetzt noch zu tun bleibt, nachdem wir die Symptome der "Technischen Schulden" als KPIs messen und deren Root Causes fokussieren und effektiv reduzieren, ist – s. Abb. 12 – zu zeigen, dass sich die Behebung von "Technischen Schulden" auszahlt.

Zum einen können wir Einzelarbeiten zur Behebung der "Technischen Schulden" wie Bugs und Features als Issues aufnehmen, beschreiben und motivieren. Das hat den Vorteil, dass man damit den Verbesserungsaufwand, wie bereits vorgestellt, genauso messen und in Relation zum Wartungsaufwand und dem Feature-Entwicklungsaufwand setzen kann. Dabei gilt die Daumenregel, dass Wartungsaufwand fürs Bugfixing und der Verbesserungsaufwand bei regelmäßiger Anwendung nicht mehr als ca. 20-30 Prozent ausmachen dürfen. Bei geringerem Aufwand bzw. dessen Aussetzen zur Behebung der "Technischen Schulden" über mehrere Iterationen lässt sich damit messbar darlegen, wie viel an Behebungsaufwand mittlerweile versäumt wurde und auf die sich aufstauenden Risiken in Zahlen hinweisen. Und zu guter Letzt ist zu prüfen, ob sich die schlechten Werte der Symptom-KPIs zum Besseren wenden und in welcher Größenordnung. Damit lässt sich zeigen, dass sich der Behebungsaufwand lohnt oder bei einer ausbleibenden Trendumkehr ist bewusst die Frage zu stellen, warum der Effekt nicht eintritt.

Das “Atommodell” zu Technischen Schulden

Zum Abschluss sei noch das "Atommodell" der "Technischen Schulden" erwähnt, in dem wir versucht haben, alle Konzepte prägnant und bildhaft zusammenzufassen.

Der Kern sind die unterschiedlichen Bereiche der "Technischen Schulden" in der Software bzw. deren Entwicklung. Diese können im Code, in der Architektur oder bei den Tests bzw. der Dokumentation oder den Engineering-Prozessen liegen. Die äußerste Hülle dagegen umfasst die Business-Konsequenzen hoher "Technischer Schulden" wie "Budget overrun", "Missed launch dates" oder "Angry customers". Die Business-Konsequenzen wollen wir auf den Kreis der Symptome und deren KPIs zurückführen, um diese wiederum mit den Problem-Ursachen, den Root Causes, zu korrelieren. So dass wir einen Einfluss auf diesen Kern nehmen können, wenn er auch nicht komplett zerschlagen werden kann, da "Technische Schulden" immer vorhanden sein werden. Aber wir wollen Überzeugungsarbeit leisten, um ein Minimum an Budget für einen effektiven und nachvollziehbaren Umgang damit zur Verfügung zu haben.

Mittels der Software-Analyse ist die Erfassung der KPIs und die Durchführung einer Root-Cause-Analyse zur Kosten/Nutzen-Erstellung bzw. der Technischen Schulden und deren Behebung in laufenden Software-Projekten kontinuierlich durchführbar. Genauso ist damit eine effektive Bestandsaufnahme in Due-Diligence-Projekten bei der Akquisition von Software-Assets und beim Einkauf zur Auswahl und zum Monitoring von Software-Dienstleistern und deren Lieferungen von Belang möglich.

Quellen
  1. Egon Wuchner & Konstantin Sokolov, Informatik Aktuell: (Collective) Code Ownership neugedacht
  2. Blog Cape of Good Code: Corona Warn App - Auf dem Weg zu kritischen Technischen Schulden?

Icons der Abbildungen von : cleanpng.com, pinterest.de, flaticon.com, icon-library.com, pixabay.com, pinclipart.com 

Autoren

Konstantin Sokolov

Konstantin Sokolov arbeitet seit 2013 im Bereich der Softwareanalysen mit Egon Wuchner zusammen. 2018 haben sie zusammen Cape of Good Code gegründet. Als CTO ist Konstantin nicht nur für die Entwicklung der DETANGLE Analyse Suite…
>> Weiterlesen

Egon Wuchner

Egon Wuchner bringt mehr als 20 Jahre Berufserfahrung im Bereich Software Engineering als Entwickler, Software-Architekt, Forscher und Projektleiter mit.
>> Weiterlesen
Das könnte Sie auch interessieren

Kommentare (0)

Neuen Kommentar schreiben