Der Kunde fragt: "Wenn das Hinzufügen dieser Funktion in einer neuen Anwendung eine Woche dauern würde, warum braucht es hier drei Wochen?"
Das ist eine sehr gute Frage.
Und die Antwort lautet oft nicht: "weil die Programmierer langsamer arbeiten".
Das Problem kann viel tiefer liegen - in der Systemarchitektur, seinen Abhängigkeiten, der Art der Datenspeicherung, fehlenden Tests, historischen Entscheidungen und weiteren über Jahre hinzugefügten Änderungen.
Genau deshalb sind die Kosten der Softwareentwicklung nicht konstant.
Dieselbe Funktion kann in zwei verschiedenen Systemen völlig unterschiedliche Kosten verursachen.
Code wird nicht nur nach der Anzahl der Funktionen bewertet
Auf den ersten Blick kann eine Aufgabe banal wirken.
"Fügen wir die Möglichkeit hinzu, Daten nach Excel zu exportieren."
Oder: "Fügen wir eine neue Benutzerrolle hinzu."
Oder: "Verbinden wir das System mit unserem CRM."
Das Problem ist, dass eine Funktion nie völlig losgelöst vom Rest des Systems existiert.
Neue Funktionalität kann Änderungen erfordern an:
- der Datenbank,
- der API,
- dem Backend,
- dem Frontend,
- dem Berechtigungssystem,
- dem Logging,
- dem Reporting,
- den Integrationen,
- den Tests,
- den Cache-Mechanismen,
- der Dokumentation,
- dem Bereitstellungsprozess.
Je stärker ein System verknüpft ist, desto mehr Elemente müssen vor einer Änderung analysiert werden.
Die größten Kosten können vor dem Schreiben der ersten Codezeile entstehen
In einem ausgereiften System sollte ein Entwickler nicht einfach anfangen zu programmieren.
Zuerst muss man beantworten:
- Wo sollte diese Funktion hinzugefügt werden?
- Mit welchen Modulen wird sie kommunizieren?
- Welche Daten verwendet sie?
- Decken die bestehenden Berechtigungsmechanismen sie ab?
- Wird sich die Änderung auf andere Prozesse auswirken?
- Welche Tests müssen aktualisiert werden?
- Erlaubt die aktuelle Architektur überhaupt, dies korrekt umzusetzen?
All das ist Teil der Kosten der Funktionsentwicklung.
Deshalb kann in einem alten System ein erheblicher Teil der Arbeit nicht das Programmieren selbst sein, sondern das Erkennen von Abhängigkeiten und Einschränkungen der bestehenden Lösung.
Technische Schulden wirken wie Zinsen
Ein guter Weg, technische Schulden zu verstehen, ist genau der Kostenpunkt weiterer Änderungen.
Wenn eine bestimmte Lösung früher schnell umgesetzt wurde, kann das völlig sinnvoll gewesen sein.
Das Problem entsteht, wenn die temporäre Lösung zu einem festen Bestandteil des Systems wird.
Eine weitere Funktion entsteht.
Dann noch eine.
Ein Sonderfall taucht auf.
Dann noch ein Sonderfall.
Dazu kommen Integration, ein Workaround, ein manueller Prozess und eine zusätzliche Regel.
Nach einigen Jahren erinnert sich niemand mehr daran, warum das System genau so funktioniert.
Aber jede weitere Änderung muss all diese historischen Entscheidungen berücksichtigen.
Martin Fowler beschreibt technische Schulden als den zusätzlichen Aufwand, der bei Systemänderungen aufgrund von Problemen mit der internen Qualität des Systems entsteht.
Man kann also sagen: Technische Schulden müssen die Entwicklung nicht sofort stoppen. Zuerst machen sie jede weitere Änderung teurer.
Signal eins: "nebenbei müssen noch fünf weitere Dinge behoben werden"
Das ist eines der charakteristischsten Anzeichen.
Der Kunde bestellt eine Funktion.
Bei der Analyse stellt sich heraus, dass für die Umsetzung Folgendes nötig ist:
- die Tabellenstruktur zu korrigieren,
- die Art der Autorisierung zu ändern,
- die Bibliothek zu aktualisieren,
- das alte API zu reparieren,
- einen Teil des Frontends neu zu schreiben.
Plötzlich ist aus einer kleinen Funktion keine kleine Funktion mehr geworden. Nicht, weil die Anforderung kompliziert ist. Sondern weil dem System die passenden architektonischen Grenzen fehlen.
Signal zwei: Eine Änderung erfordert Tests des gesamten Systems
Wenn eine kleine Anpassung einen vollständigen manuellen Regressionstest erfordert, zahlt die Organisation für fehlende Automatisierung.
Mit dem Wachstum des Systems steigt die Zahl möglicher Kombinationen.
Ohne einen geeigneten Testsatz wird es immer schwieriger sicher zu sein, dass die neue Funktion die alte nicht beschädigt hat.
Das wiederum führt zu Vorsicht.
Deployments werden seltener.
Änderungen werden größer.
Das Risiko steigt.
Und größere Deployments sind im Problemfall schwieriger zu diagnostizieren.
Es entsteht ein Teufelskreis.
Signal drei: "Dieses Modul lieber nicht anfassen"
Dieser Satz sollte eine Warnlampe aufleuchten lassen.
Wenn ein bestimmtes Modul zu einem Bereich geworden ist, den das Team meidet, weil sein Verhalten unvorhersehbar ist, hat das System ein erhebliches Wartungsproblem.
Noch schlimmer ist es, wenn nur eine Person weiß, wie es funktioniert. Dann hat das Unternehmen nicht nur technische Schulden. Es hat auch Knowledge Risk.
Der Weggang eines Mitarbeiters kann den Verlust des Wissens bedeuten, das für die sichere Weiterentwicklung des Systems nötig ist.
Signal vier: Jede Funktion erfordert Ausnahmen
Ein gut gestaltetes System sollte vorhersehbare Regeln haben.
Wenn jede weitere Funktion das Hinzufügen einer speziellen Ausnahme, einer zusätzlichen Bedingung oder eines individuellen Pfads erfordert, bremst die Architektur wahrscheinlich die Entwicklung.
Das führt oft zu Code, dessen Verhalten sich nicht mehr leicht vorhersagen lässt.
Und fehlende Vorhersagbarkeit bedeutet höhere Kosten für Analyse, Tests und Wartung.
Muss man alles neu schreiben?
Nein.
Und hier kommen wir zu einer sehr wichtigen Unterscheidung.
Technische Schulden bedeuten nicht automatisch, dass ein Rewrite nötig ist.
Mögliche Lösungen sind:
Refactoring
Also die Verbesserung der Struktur des bestehenden Codes, ohne sein fachliches Verhalten zu ändern.
Das ist ein guter Weg, wenn das System grundsätzlich noch eine sinnvolle Architektur hat, einzelne Teile aber schwer zu warten sind.
Modernisierung ausgewählter Komponenten
Man muss nicht die gesamte Anwendung austauschen.
Man kann mit dem problematischsten Modul, der Integration oder der Schicht beginnen.
Schrittweise Migration
Neue Elemente können neben dem alten System laufen, und weitere Bereiche werden nach und nach migriert.
Dieser Ansatz reduziert das Risiko einer einmaligen Migration. In der Literatur zur Legacy-Modernisierung wird häufig genau das schrittweise Ausgliedern von Funktionalität und das Ersetzen einzelner Teile des Systems verwendet.
Rewrite
Der Aufbau eines neuen Systems ist dann sinnvoll, wenn die aktuelle Architektur so einschränkend ist, dass weitere Modernisierung keinen gerechtfertigten Nutzen mehr bringt.
Ein Rewrite sollte jedoch eine Entscheidung auf Basis einer Analyse sein und nicht eine Reaktion auf die Frustration des Teams.
Wann lohnt es sich noch nicht, in die Modernisierung zu investieren?
Technische Schuld ist an sich kein Grund, die Weiterentwicklung zu stoppen. Jedes System hat ein gewisses Maß an technischer Schuld. Manchmal ergibt es wirtschaftlich keinen Sinn, sie abzutragen.
Wenn die Anwendung:
- stabil läuft,
- sicher ist,
- nur wenige Änderungen hat,
- einen Prozess unterstützt, der sich nicht wesentlich weiterentwickeln wird,
- keine betrieblichen Probleme verursacht,
kann es sinnvoll sein, sie in ihrem aktuellen Zustand zu belassen.
Es geht nicht darum, dass jedes System technologisch perfekt sein muss.
Es geht darum, dass das Maß an Schuld eine bewusste Entscheidung ist.
Wann wird die Kosten der Schuld zu einem Geschäftsproblem?
Dann, wenn sie beginnt, die Geschäftsergebnisse zu beeinflussen.
Zum Beispiel:
Eine neue Funktion sollte in einem Monat auf den Markt kommen, braucht aber drei.
Die Integration mit einem neuen Partner verzögert sich, weil die API des alten Systems die neuen Daten nicht einfach verarbeiten kann.
Die Schlüsselperson im Team muss jedes Mal mitarbeiten, weil nur sie das alte Modul kennt.
Jede größere Einführung erfordert stundenlange Regressionstests.
Ein Konkurrent führt schneller neue Funktionen ein, weil seine Plattform schnelleres Experimentieren ermöglicht.
In diesem Moment ist technical debt kein Problem mehr der IT-Abteilung.
Sie wird zu einem Geschäftsproblem.
Wie misst man, ob sich die Situation verschlechtert?
Man muss kein kompliziertes KPI-System aufbauen.
Es lohnt sich, einige einfache Kennzahlen zu beobachten:
Lead Time - wie viel Zeit zwischen dem Beginn der Arbeit an einer Änderung und ihrer Einführung vergeht.
Bereitstellungshäufigkeit - wie oft das Team Änderungen sicher ausliefern kann.
Change Failure Rate - wie oft Bereitstellungen Probleme verursachen.
Wiederherstellungszeit - wie schnell man nach einem Ausfall zum stabilen Betrieb zurückkehren kann.
Durchlaufzeit einer Funktion - ob ähnliche Aufgaben zunehmend mehr Aufwand erfordern.
Außerdem lohnt es sich, die Anzahl manueller Vorgänge, die Testabdeckung, die Aktualität der Abhängigkeiten und die Zeit zu analysieren, die benötigt wird, um einen neuen Entwickler ins Projekt einzuarbeiten.
Solche Daten zeigen, ob das Problem tatsächlich technischer Natur ist oder eher aus dem Prozess, den Anforderungen oder der Art der Arbeitsorganisation resultiert.
Die schlimmste Lösung ist "noch ein schneller Fix"
Wenn das Team weiß, dass die Architektur Änderungen braucht, das Thema aber jedes Mal aufschiebt, kann das System in eine Spirale geraten.
"Machen wir erstmal einen Workaround."
"Die Refaktorisierung machen wir später."
"Vorerst reicht das."
"Beim nächsten Release."
Das Problem ist, dass das nächste Release weitere Anforderungen mit sich bringt.
Und jeder weitere Workaround erhöht die Kosten der nächsten Änderung.
Deshalb sollte die Entscheidung, technische Schuld abzubauen, Teil der Produktentwicklungsstrategie sein und keine zufällige Reaktion auf eine Krise.
Eine gute Anwendung ist nicht eine, die niemals altert
Jedes System wird sich verändern.
Technologien werden sich verändern.
Kunden werden neue Bedürfnisse haben.
Neue Integrationen werden entstehen.
Die Arbeitsweise des Unternehmens wird sich ändern.
Deshalb sollte das Ziel nicht sein, eine Anwendung zu schaffen, die niemals modernisiert werden muss. Das Ziel sollte sein, eine Architektur zu schaffen, in der Modernisierung möglich ist, ohne den Geschäftsbetrieb zu stoppen. Das ist ein riesiger Unterschied.
Denn das beste System ist nicht das, das am Tag des Starts am modernsten aussieht. Es ist das System, das auch nach einigen Jahren dem Unternehmen ermöglicht, schnell auf Veränderungen zu reagieren.
Und wenn jede neue Funktion immer mehr kostet, bedeutet das nicht immer, dass die Funktion schwierig ist.
Vielleicht ist bereits das System selbst schwierig geworden.



