Die Softwareindustrie steckt in ihrer ganz eigenen Schuldenkrise: sie hat über Jahre Mengen an Software entwickelt ohne korrekte Qualitätskontrollprozesse einzusetzen und entsprechend viele versteckte Mängel angehäuft. Gartner Research erwartet, dass in diesem Jahr die globalen technischen Schulden in IT und Embedded-Software auf eine Billion US-Dollar anwachsen. Technische Schulden sind dabei eine Metapher für versteckte Mängel, die in der Architektur, im Design oder der Entwicklung stecken. Den Begriff der technischen Schulden hat Ward Cunningham 1992 geprägt. Er bezieht sich auf die akkumulierten Verbindlichkeiten, die entstehen, wenn Unternehmen Design und Tests abkürzen, um kurzfristige Ziele zu erreichen. Wie die finanziellen Schulden sind auch die technischen Schulden im Wesentlichen ein Darlehen mit anfallenden Zinsen. Wer das Darlehen nicht rechtzeitig zurückzahlt, kann seine Software letztendlich nicht mehr am Laufen halten. Je höher die technischen Schulden sind, desto geringer ist die Möglichkeit für Innovationen und dafür, auf dem Markt wettbewerbsfähig zu bleiben.

Eckdaten

So wie sich monetäre Schulden zu einem erdrückenden Berg auftürmen können, sammeln sich auch kleinere und größere Mängel in Embedded-Software an. Statt den vorhandenen Code vollständig zu testen und möglichst viele Fehler zu beseitigen, wird er möglichst früh freigegeben und in Produkte eingesetzt. Das rächt sich im Laufe der Zeit. Der Testspezialist Vector Software zeigt hier einen systematischen Weg auf, um diese technischen Schulden sukzessive abzutragen.

Ein Großteil der technischen Schulden entsteht durch den Geschäftsdruck, immer neue Produkte auf den Markt zu bringen. Die vier häufigsten Gründe für technische Schulden sind: schlechte Architekturauswahl, übermäßig komplexer Code, Fehlen von Codedokumentation sowie unzureichendes Testen.  Mit dem Anstieg der technischen Schulden verbringen die Entwickler immer mehr ihrer Zeit mit der Fehlerbehebung und mühen sich mit fragilem Code ab, statt neue Funktionen zu erstellen.

Die Grenzen der Regulierung

Da unser tägliches Leben abhängig von eingebetteter Software geworden ist, ist der Bedarf an solidem und unbelastetem Code groß, insbesondere wenn die Sicherheit der Nutzer oder der Umgebung gefährdet ist. Sicherheitskritische Branchen wie Luftfahrt, Eisenbahn und Automobil haben Entwicklungsstandards und Vorschriften entwickelt. Die sollen sicherstellen, dass alle Anwendungen vor dem Einsatz vollständig getestet wurden. Ziel dieser Standards sind robuste, fehlertolerante Anwendungen.

Während es keinen Zweifel gibt, dass das Einhalten von Vorschriften, wie den Safety Integrity Levels (SILs) oder Development Assurance Levels (DALs), hoch qualitative Software als Ergebnis hat und somit die technischen Schulden durch „unzureichendes Testen” vermeidet, zielen sie kaum auf die anderen drei Hauptgründe für technische Schulden ab.

Technische Schulden abtragen

Wenn die weltweiten technischen Schulden ein Billionen-Dollar-Problem sind, wie Gartner prophezeit, oder doch nur ein Zehntel davon ausmachen: in keinem Fall ist schnelle Abhilfe zu erwarten. Statt nach einer einfachen Lösung zu suchen, müssen Softwareentwickler und Hersteller neue Entwicklungsprozesse aufsetzen, die sicherstellen, dass sie keine neuen Schulden hinzufügen und vorhandene Schulden sukzessive abtragen. Zwar gibt es verschiedene Wege um dies zu realisieren, doch die folgenden Schritte sind immer nötig:

  • Das Problem verstehen: Kennzahlen erfassen, etwa Codekomplexität, Kommentardichte und API-Komplexität.
  • Ergebnisse teilen: die Daten allen Stakeholdern zur Verfügung stellen.
  • Verbesserungen planen: zum Beispiel den Aufwand auf Sonderfälle fokussieren, etwa Funktionen höchster Komplexität aufteilen.

Um mit der Entwicklung der benötigten Menge an Software fortzufahren, müssen die Unternehmen skalierbare Entwicklungsprozesse erstellen, die zu robusten und wartbaren Codebasen führen, die relativ frei von technischen Schulden sind. Solange moderne Herstellungsmethoden, wie das Minimum Viable Product (MVP), akzeptieren, dass die erste Version niemals fehlerfrei ist und Marktdruck bedeutet, dass es immer eine gewisse Menge an technischen Schulden gibt, sollte das Ziel jedes Entwicklungsteams sein, die technischen Schulden auf einem zu bewältigenden Level zu halten.

Währung für technische Schulden

Um den aktuellen Stand der technischen Schulden zu erkennen,  können Entwickler Kennzahlen sammeln und Testergebnisse aufzeichnen. Um diese Daten zu verstehen, müssen die Unternehmen eine Reihe von umfassenden und gemeinsamen Kennzahlen und Grenzwerten einführen, die verdeutlichen, was „schuldenfrei“ hier bedeutet. Ebenso müssen sie einen automatisierten Weg zur Aufzeichnung und zum Report dieser Kennzahlen einrichten. So schaffen sie eine Ist-Basis. Da Softwarekomponenten häufig an verschiedenen Orten und von verschiedenen Teams entwickelt werden, ist es sehr wahrscheinlich, dass die Basiskennzahlen innerhalb einer Codebasis sehr stark variieren.

Bild 1: Nur gemeinsam mit Funktionstests und Unit-Tests ist es möglich, jede einzelne Programmzeile mit mindestens einem Testfall auszuführen.

Bild 1: Nur gemeinsam mit Funktionstests und Unit-Tests ist es möglich, jede einzelne Programmzeile mit mindestens einem Testfall auszuführen.Vector Software

Obwohl diese statische Analyse einige der Schuldenkennzahlen liefern kann, misst sie nicht die Testvollständigkeit und hilft somit auch nicht die Korrektheit zu bestimmen. Das Messen der Testvollständigkeit ist eine Schlüsselkomponente um den gesamten Umfang der technischen Schulden abzubilden. Die Vollständigkeit kann man am einfachsten mit der Codeabdeckungsanalyse messen (Code-Coverage-Analyse, Bild 1). Sie zeichnet auf, wie viel des vorhandenen Codes ein Testfall tatsächlich durchlaufen hat. Durch Verzweigungen, Schleifen und Subroutinen ist das meist nur ein geringer Wert pro Testfall. Erst die Summe vieler Tests kann komplexe Programme umfassend überprüfen.

Testvollständigkeit erhöhen

Sobald sie die Codekomplexität und Dokumentationsmängel reduziert haben, sollten sich Entwickler um die häufigste Ursache für technische Schulden kümmern: unzureichendes Testen. Ähnlich wie bei den Kennzahlen und Grenzwerten, ist der erste Schritt zu entscheiden, was adäquates Testen für das jeweilige Unternehmen bedeutet. Sinnvoll erscheint, auf Korrektheit zu bestehen, also dass die Anwendung wie gewünscht funktioniert, sowie auf Robustheit, also dass der Code auch Bedingungen außerhalb der Norm vernünftig handhabt.

Bild 2: MC/DC-Tests (Modified Condition / Decision Coverage) sollen nicht nur jede Codezeile erfassen, sondern auch jede Bedingung triggern und jede mögliche Entscheidung abdecken.

Bild 2: MC/DC-Tests (Modified Condition / Decision Coverage) sollen nicht nur jede Codezeile erfassen, sondern auch jede Bedingung triggern und jede mögliche Entscheidung abdecken.Vector Software

Kürzlich gab es viel Aufregung über das Jahr-2038-Problem. Wie beim Y2K, werden sich im Jahr 2038 alle Betriebssysteme überschlagen, die einen 32-Bit-Integer-Wert nutzen, um die Anzahl der Sekunden seit dem 1. Januar 1970 nachzuvollziehen. Dieses Problem des 32-Bit-Integer-Überlaufs ist das gleiche Problem, das in den Boeing-787-Softwaresystemen im Sommer 2015 entdeckt wurde. Wenn das System länger als 248 Tage am Stück läuft, wechselt es automatisch in den ausfallsicheren Modus und schaltet die Triebwerke ab: die Anzahl an Millisekunden in 248 Tagen lässt sich nicht mehr als 32-Bit-Integer darstellen. Solche Situationen lassen sich vermeiden, wenn man Robustheitstests rigoroser durchführt. Sie müssen die Leistung bei Minimum, Mittelwert und Maximum sowie die Funktionsdatengrenzen der Anwendung überprüfen (Bild 2).

On-Target-Testen ist ein essentielles Element für eingebettete Anwendungen, um sicherzustellen, dass die Software vernünftig funktioniert wenn sie auf den Markt kommt. Dabei sollte die Testumgebung so nah wie möglich an der Produktionsumgebung sein, inklusive der gleichen Version aller Compiler, Firmware und Hardware. Eine gemeinsame Testplattform macht es dennoch einfach, Korrektheits- und Robustheitstests zu implementieren. Doch bei den Millionen von unzureichend getesteten Anwendungen kann keine Firma die nötigen Tests nachträglich per Hand schreiben.

Testfälle automatisch erzeugen

Automatisierte Testfallgenerierung kann die Testvollständigkeit von Legacy-Anwendungen dennoch erhöhen. Tests lassen sich vom Interface und auch von der Logik der Anwendung aus generieren. Automatisierte Interface-Tests erzeugen eine Kombination von Werten, die für die Grenzarten der Parameter spezifisch sind (zum Beispiel Min- und Max-Integer). Automatisierte Logik-Tests wiederum sollen jeden Entscheidungspunkt in der Anwendung dazu zu bringen, sowohl True- als auch False-Ergebnisse anzunehmen. Obwohl automatisierte Tests die Korrektheit nicht beweisen, helfen sie auf einfache Weise, obsoleten, ungenutzten oder doppelten Code zu finden, der sich über Jahre durch Verbesserung und Erweiterung der Software angesammelt hat.

Bild 3: Wenn Entwickler nur eine Stelle im Quellcode ändern (grün), hat das auf die meisten Testfälle keinerlei Auswirkung. Change-based Testing führt nur die betroffenen Tests aus (orange).

Bild 3: Wenn Entwickler nur eine Stelle im Quellcode ändern (grün), hat das auf die meisten Testfälle keinerlei Auswirkung. Change-based Testing führt nur die betroffenen Tests aus (orange).Vector Software

Der Schlüssel zum Reduzieren technischer Schulden in Legacy-Anwendungen ist, die Komponenten im Laufe der Zeit zu refaktorieren. Jedoch wird dieser Aufwand nur fruchten, wenn es einen einfachen Weg gibt, die Testumgebung auszuführen, die das korrekte Verhalten der Komponente beweist. Automatisierte Testgenerierung kann diese Basis-Testreihe schnell bereitstellen, um existierendes Verhalten zu erfassen. Damit stellt sie sicher, dass man beim Refactoring keine Rückschritte macht und mehr Fehler einfügt als behebt. Dummerweise dauert ein Testlauf in komplexen Projekten viele Stunden oder gar Tage: Hier nach jeder Änderung einen kompletten Testlauf zu starten ist nicht zielführend. Automatisierte Workflows können aber herausfinden, welche konkreten Testfälle von einer Änderung tatsächlich betroffen sind, und nur diese ausführen (Bild 3). Damit sinkt der Testaufwand enorm und Entwickler können auch bei trivialen Änderungen sofort sicherstellen, dass sie damit keinen Test zum Scheitern bringen.

Den Überblick behalten

Abschließend brauchen Entwickler noch ein Berichtswerkzeug, das den allgemeinen Zustand der Release-Bereitschaft ihrer Codebasis präsentiert, jegliche Testlücken identifiziert und langfristige Qualitätstrends aufzeigt. Damit können Manager die Ressourcen abschätzen, die sie einsetzen müssen um die technischen Schulden abzutragen. Diese Schulden einfach zu ignorieren wäre gefährlich. Schuldenabbau wird nur mit einem realistischen Plan möglich, der neue Schulden verhindert und die Schulden im Laufe der Zeit Schritt für Schritt reduziert.