Über den Umgang mit Fehlern in der modernen Softwareentwicklung

Vom Irrtum zum Erfolg

Vom Irrtum zum Erfolg

Über den Umgang mit Fehlern in der modernen Softwareentwicklung

Vom Irrtum zum Erfolg


Software ist, abgesehen von wirklich trivialen Programmen, umfassend und komplex zugleich. Das bedeutet auch, dass es nach heutigem Kenntnisstand keine fehlerfreien Anwendungen gibt. Dennoch tun wir uns schwer, mit Fehlern umzugehen, und wir beseitigen sie möglichst schnell und ohne Aufsehen zu erregen. Damit vertun wir eine Möglichkeit, dazuzulernen – und zwar sowohl für uns selbst als auch für das gesamte Entwicklungsteam.

Beginnen wir diesen Artikel mit einem Auszug aus einem Bericht des Portals SPIEGEL ONLINE vom 14. Oktober 2018. Zitiert wird dort eine Studie der Unternehmensberatung Ernst & Jung. In dieser Studie hat ein beachtlicher Teil der Angestellten angegeben, dass Fehler in Unternehmen nicht angesprochen werden. Hauptgründe für das Vertuschen von Fehlern sind nach Einschätzung der Befragten Angst vor Nachteilen für die eigene Karriere oder die Sorge, den Job verlieren zu können. Wie steht es im Bereich der Softwareentwicklung um die Fehlerkultur? Wie gehen wir damit üblicherweise um? Sehen wir zu, dass die Fehler möglichst klammheimlich korrigiert werden, bevor jemand etwas davon mitbekommen hat, oder diskutieren wir die Ursachen und die Beseitigung von Fehlern offen? In diesem Artikel wollen wir uns dem Thema aus verschiedenen technischen und nicht technischen Perspektiven nähern und daraus Schlüsse ziehen, wie heutzutage ein adäquater Umgang damit aussehen kann. Beginnen wir mit den möglichen Folgen von Softwarefehlern.

Kleiner Fehler, große Wirkung

Nicht alle Softwarefehler haben drastische Folgen. Gern publiziert werden die folgenden Fehler und ihre Wirkungen:

  • Absturz der Ariane 5: Ein Softwarefehler wurde durch eine Konvertierung einer 64-Bit-Fließkommazahl in einen 16-Bit-Integerwert verursacht.
  • Schokoladenberg: Das ERP-System des Schokoladenherstellers Cadbury verursachte durch einen Bug eine Überproduktion von Schokoladenriegeln, die nicht abgesetzt werden konnten. 48 Millionen Euro soll der Fehler gekostet haben – ein süßer Albtraum.
  • Probleme bei Mauteinführung: Die technische Umsetzung
    war zu 2003 geplant. Gestartet ist das System erst drei Jahre später. Die Ausfälle in den Einnahmen belaufen sich auf 3,5 Mrd. Euro.

Grundsätzlich ist jeder Softwarefehler ein Ärgernis für den Kunden. Das System arbeitet nicht so, wie es soll. Im betrieblichen Umfeld werden zum Beispiel Produktionsprozesse gestört; bei der privaten Nutzung muss der Anwender mit Einschränkungen und Unzulänglichkeiten leben.

Arten von Fehlern in der Softwareentwicklung

Was ist eigentlich ein Fehler in Software? Diese triviale Frage führt zu folgenden, nicht ganz einheitlichen Definitionen:

  • Nach DIN 66271: Ein Fehler ist die Nichterfüllung einer festgelegten Forderung, zum Beispiel die Abweichung von einem erwarteten Merkmalswert, und er führt zu Differenzen zwischen den Vertragsparteien.
  • EN ISO 9000:2005: Ein Fehler ist die Nichterfüllung einer Forderung. Ein Mangel ist die Nichterfüllung einer Forderung hinsichtlich des beabsichtigten oder festgelegten Gebrauchs. Mängel haben rechtliche Folgen im Sinne der Produkthaftung.
  • DIN 55350 Teil 31: Ein Fehler ist die Nichterfüllung vorgegebener Forderungen durch einen Merkmalswert, zum Beispiel ein Maß außerhalb eines Toleranzbereichs, wobei die Verwendbarkeit nicht notwendigerweise beeinträchtigt ist.
  • IEEE Standard 1044-1993 Classification for Software Anomalies: Ein Fehler (engl. Anomaly) ist ein Zustand, der von den Erwartungen oder Erfahrungen abweicht. Diese Abweichung kann sich auf Anforderungsspezifikationen, Designdokumente, Benutzerdokumente, Standards etc. beziehen.
  • Telecom Standard TL9000: Dieser branchenspezifische Standard ist eine ISO-9001-Erweiterung. Der Begriff des Fehlers ist nicht explizit definiert. Dennoch wird zwischen technischem Fehlverhalten (Hardware, Software etc.) und Handhabungsproblemen unterschieden, die beispielsweise aus inkorrekten Systemeingaben resultieren.
  • Six Sigma: Ein Managementsystem zur Prozessverbesserung, ein statistisches Qualitätsziel und zugleich eine Methode des Qualitätsmanagements. Demnach ist ein Fehler ein unerwartetes Verhalten, das nicht bei der Entstehung festgestellt, sondern erst in folgenden Entwicklungsphasen entdeckt wird, aber noch bevor das Produkt durch den Nutzer eingesetzt wird. Wird die Software mit dem Fehler an den Kunden ausgeliefert, spricht man von einem Defekt.

Und welche Definition ist für uns Entwickler heute am besten greifbar? Wir verwenden die folgende Begrifflichkeit: Unter einem Fehler in einem Softwaresystem verstehen wir eine Abweichung von einem gewünschten Sollzustand. Nicht jeder Fehler muss zu einer Unbenutzbarkeit der Anwendung führen. Dabei muss man immer den Zeitpunkt der Vereinbarung zwischen den Vertragsbeteiligten zugrunde legen. Ein Beispiel: Wurde für eine Software vor fünfzehn Jahren ein benutzerfreundliches User Interface (UI) vereinbart, kann dieses nicht mit den Maßstäben für ein UI aus dem Jahr 2018 beurteilt werden. Damals waren Touch-Interaktion und heutige Designvorgaben noch nicht bekannt. Man kann deshalb die Software nicht als fehlerbehaftet ansehen. An diesem Beispiel mag die Beurteilung noch einfach sein. Was ist mit Anwendungen, die für Windows XP geschrieben wurden? Wenn eine solche Software unter Windows 10 nicht ohne Probleme läuft, liegt dann ein Fehler vor? Sofern das Projekt und der Vertrag keine kontinuierliche Anpassung an die technische Entwicklung vorsehen, haben wir es nicht mit einem Fehler zu tun. Wir lernen daraus: Heute problemlos funktionierende Software kann aufgrund des technischen Fortschritts morgen durchaus zu Fehlern führen. Es kommt darauf an, ob wir als Hersteller zur Anpassung verpflichtet sind. Der Begriff Fehler hat damit nicht nur eine technische Dimension, sondern auch Bedeutung aus vertraglicher Sicht zwischen Hersteller und Nutzer einer Software.

Man unterscheidet mehrere Arten von Fehlern. Grobe Kategorien sind Syntaxfehler, logische Fehler, Laufzeit und Designfehler. Sehen wir uns diese Fehlerarten etwas genauer an:

  • Syntaxfehler: Verstöße gegen die Regeln der Programmiersprache. In den meisten Fällen kann diese Art von Fehlern bereits während der Programmerstellung abgefangen werden. Der Programmcode kann nicht kompiliert werden. Der Fehler betrifft i. d. R. nur den Entwickler, also diejenige Person, die direkt am Quellcode arbeitet. Die modernen Entwicklungsumgebungen inklusive der Compiler geben Hinweise darauf, an welchen Stellen im Programmcode etwas im Argen liegt und korrigiert werden muss.
  • Laufzeitfehler: Diese Fehlerart umfasst alle Arten von Fehlern, die während der Programmausführung auftreten. Typische Beispiele sind Wertebereichsüberschreitungen bei Variablen, Zuweisung von Werten bei nicht initialisierten Objekten, falsche Datentypen usw. Besondern häufig und auch schwerer zu entdecken sind Fehler dieser Art beim Datenaustausch zwischen unterschiedlichen Programmteilen, d. h. an den Schnittstellen von Systemen. Werden die übergebenen Daten dabei nicht auf ihre Zulässigkeit geprüft, kann es schnell zu Fehlern während der Programmausführung kommen. Bei der Aktualisierung einzelner Programmbibliotheken muss darauf geachtet werden, dass die Schnittstellen (APIs) sich nicht ändern.
  • Logische Fehler: Das sind Fehler, die in den Algorithmen der Programme begründet sind. Falsche Ablaufstrukturen, fehlerhafte Berechnungen usw. fallen in diesen Bereich. Logische Fehler lassen sich relativ leicht durch ein umfassendes Testen der Software entdecken. Darauf kommen wir später nochmal zurück.
  • Designfehler: Diese Fehler sind im zugrunde liegenden Konzept begründet und können entstehen, wenn beispielsweise von falschen Anforderungen ausgegangen wird. Auch dass sich Anforderungen zu schnell ändern und nicht in eine Überarbeitung des Konzepts eingeflossen sind, kann eine Ursache für Designfehler sein. Um das Dilemma einer zu statischen Anforderungsdefinition und der dynamischen Anpassungsnotwendigkeit in der Praxis in den Griff zu bekommen, haben sich agile Konzepte in der Softwareentwicklung durchgesetzt. Ein Bestandteil dieser Ansätze sind auch Retrospektiven, d. h. die nachträgliche Betrachtung des Workflows und damit ein Bestandteil einer modernen Fehlerkultur.

Fassen wir also zusammen: Fehler sind genauer nach ihrer Art zu klassifizieren.

Fehlerklassen als Maßstab für die Wichtigkeit der Behebung

Dass es keine fehlerfreie Software gibt, ist also klar. Wir wissen aber auch: Manche Fehler wiegen einfach schwerer als andere. Um die Wichtigkeit von Fehlern einzuteilen, arbeitet man mit Fehlerklassen. Eins gleich zu Beginn: Es gibt keine einheitliche Norm für eine Fehlerklassifizierung. Unterschiedliche Ziele der Klassifizierung führen zu ähnlichen, aber im Detail unterschiedlichen Definitionen (Tabelle 1).

Standard Fehlerklassifikation
DIN 66271 Die Klassifizierung ist an der Bewertung der Fehlerfolgen ausgerichtet und unterscheidet nach der Beeinträchtigung des Einsatzes und dem Schadensrisiko drei Stufen: hoch, mittel und niedrig.
DIN 55350 Teil 31 Die Einteilung ist an den Folgen ausgerichtet. Man unterscheidet kritische Fehler (gefährliche, kritische Situationen werden geschaffen), Hauptfehler (eventueller Ausfall oder die Brauchbarkeit wird wesentlich herabgesetzt) und Nebenfehler (Brauchbarkeit oder Betrieb werden nur geringfügig beeinflusst).
Nach IEEE Standard 1044-1993 „Classification for Software Anomalies“ Die Klassifizierung wird indirekt über den aus einem Fehler resultierenden Produktstatus definiert. Das sind: unbrauchbar, degradiert, beeinträchtigt (Workaround existiert) und nicht beeinträchtigt.
Telecom Standard TL9000 Drei Fehlerklassen (critical, major und minor) werden unterschieden.
Six Sigma Zwei Fehlerklassen: A-Fehler sind fehlerhafte oder fehlende Anforderung und nicht entdeckte Bedürfnisse des Nutzers. B-Fehler sind fehlerhafte Implementierungen.
ISTQB – International Software Testing Qualification Board Es werden folgende Kategorien unterschieden: „Critical“: Dieser Fehler kann zu einem Totalausfall des Systems führen. „Major“: Die Folgen sind nahezu identisch, aber es gibt einen Workaround. „Moderate“: Das System produziert fehlerhafte Ergebnisse. Es kommt nicht zum Programmabbruch. „Minor“: Die Folgen entsprechen im Wesentlichen denen von Moderate. Das System liefert teilweise fehlerhafte Ergebnisse. Es kommt nicht zum Programmabbruch. Es gibt eine Möglichkeit, die Fehler zu umgehen. „Cosmetic“: Das Aussehen bzw. das Verhalten der Software sollten verbessert werden.

Tabelle 1: Klassifikationsansätze für Softwarefehler (Bitkom.org, insights.qa4software.de)

Fehlerdokumentation

Um einen Fehler in einer Software beseitigen zu können, muss man diesen kennen. Als Entwickler müssen wir möglichst genaue Kenntnis von den Umständen haben, wie der Fehler aufgetreten ist, und es wäre gut, wenn er reproduzierbar wäre. Bei Individualsoftware bestehen gute Chancen, dass eine Dokumentation des Fehlers durchgeführt werden kann. Beispielsweise können Anwender eine Hotline kontaktieren, die dann nach einem vorgegebenen Muster den Fehler des Kunden aufnehmen kann, oder aber der Fehler kann mithilfe eines Formulars online gemeldet werden. Ein interessanter Vorschlag zur Dokumentation von Fehlern findet sich in. Demnach sollten im Idealfall folgende Punkte dokumentiert werden:

  • Titel: Ein aussagekräftiger Fehlertitel, kurz und prägnant, d. h. keine langen Texte.
  • ID: Eine eindeutige Fehlernummer oder Fehler-ID. Die Ticketsysteme vergeben diese i. d. R. automatisch.
  • Datum: Das ist notwendig, um auch später den Zeitraum zwischen Meldung des Fehlers und der Beseitigung bestimmen zu können.
  • Versionsangaben: Programmbezeichnung mit Versionsnummer bzw. Entwicklungsstand. Das ist aus zwei Gründen sehr wichtig: Verwendet der Nutzer nicht die aktuelle Programmversion, kann der Fehler sehr schnell behoben werden, indem ein Update auf die aktuelle Version durchgeführt wird. Ist die Version hingegen aktuell, kann daraus geschlossen werden, dass der Fehler mit dem letzten Update in die Software eingebaut wurde. Es handelt sich dabei um einen unerwünschten Nebeneffekt.
  • Beschreibung: Fehlerbeschreibungen sollten möglichst inklusive Screenshots der angezeigten Fehlermeldungen sein und, wenn möglich, Logfiles und eine Beschreibung der verarbeiteten Daten beinhalten. Also alle Informationen, die für eine Reproduktion bzw. ein Nachvollziehen des Fehlers hilfreich sind. Die Frage lautet: Welche Schritte sind notwendig, um den Fehler erneut auszulösen?
  • Klassifizierung: Dient der Einordnung des Fehlers. Orientieren Sie sich dabei an den o. g. Kategorien. Wichtig: Behalten Sie ein einmal gewähltes System der Kategorisierung bei.
  • Fehlerstatus: Welchen Bearbeitungsstand hat der Fehler aktuell? Mögliche Zustände sind zum Beispiel „offen“, „in Analyse“, „in Behebung“, „im Test“, „erledigt“ oder auch „keine Maßnahme“. Diese Angaben helfen bei einer Nachfrage dabei, dem Kunden eine Einschätzung zum Bearbeitungsstand geben zu
    können.
  • Historie der Fehlerbearbeitung: Wichtig für Fehler, deren Ursachen mehrdeutig oder zunächst unklar sind. Hier muss man sich auf die Suche nach einer Lösung begeben. Als Protokoll zu den bereits durchgeführten Tätigkeiten führt man eine Art Logbuch, d. h. eine Historie der Fehlerbearbeitung. Wichtig ist eine solche Dokumentation vor allen Dingen dann, wenn man gemeinsam in einem Team arbeitet. Das Tool zur Fehlerdokumentation muss auf jeden Fall das Führen einer solchen Historie unterstützen.
  • Verantwortlicher Bearbeiter: Irgendwer muss für den gemeldeten Fehler letztendlich verantwortlich sein. Das kann unterschiedlich geregelt werden. Werden die Fehler beispielsweise in eine Taskliste eingestellt, dann ist diejenige Person zuständig, die den Fehler initial bearbeitet. Das heißt nicht, dass die verantwortliche Person auch den Fehler eigenständig beheben muss. Es heißt aber, dass sie bis zum Abschluss der Bearbeitung zuständig bleibt.
  • Hardwarekonfiguration: Software ist an Hardware gebunden. Auch wenn man heute weitgehend von der verwendeten Hardware durch den Einsatz von Frameworks und generischen Bibliotheken bei der Programmierung abstrahiert, kann diese immer noch die Quelle eines Fehlers darstellen. Typische Beispiele sind nicht kompatible Grafikkarten oder ein Bildschirm mit einer außergewöhnlichen Auflösung. Dokumentieren Sie bei der Fehleraufnahme die wichtigsten Parameter der Hardware, unter der der Fehler aufgetreten ist.
  • Konfiguration: Gemeint ist hier die Softwarekonfiguration, unter der der Fehler auftrat. Dazu gehören Angaben zur Version des Betriebssystems, zur verwendeten Systemsoftware (Treiber) oder eingesetzten Bibliotheken, die anwendungsübergreifend verwendet werden.
  • Geplante Bereinigung: Ein schwieriges Thema – insbesondere dann, wenn die Ursache des Fehlers noch unklar ist. Dennoch kann man versuchen, aufgrund von Erfahrungen eine Einschätzung des Aufwands vorzunehmen.

Das wichtigste Instrument zum Finden vorhandener Fehler sind Softwaretests. Dazu kommen wir jetzt.

Tests zur Vorbeugung und Behebung von Fehlern

Die Überschrift sagt es bereits: Tests in jeder Phase der Entwicklung sollen Fehler in einem Programm beseitigen bzw. deren Entstehung von vornherein verhindern. Ein erfolgreicher Test entdeckt möglichst viele Fehler. Jeder Fehler, der im Test entdeckt wird, bleibt in diesem Sinne dem Kunden erspart. Üblicherweise kann man zwischen den folgenden Testarten unterscheiden:

  • funktionaler Test
  • nichtfunktionaler Test
  • strukturbezogener Test
  • änderungsbezogener Test

Sehen wir uns das im Einzelnen an. Bei einem funktionalen Test wird das System auf sein Ein- und Ausgabeverhalten geprüft. Basis für die Bewertung sind die sogenannten funktionalen Anforderungen, die sich u. a. aus den Dokumenten der Anforderungsanalyse wie Lasten- und Pflichtenheft (klassische Vorgehensweise) oder dem Backlog (agile Vorgehensweise) ergeben. Meist werden diese Tests als Blackboxverfahren durchgeführt. Eine funktionale Anforderung wird in der Regel nicht durch einen einzigen Testfall, sondern durch das Zusammenspiel mehrerer Testfälle überprüft. Bei nichtfunktionalen Tests geht es im Wesentlichen darum, wie gut das untersuchte System funktioniert. Typische nichtfunktionale Tests sind: Lasttest, Performancetest, Stresstest und der Usabilitytest.

Kommen wir zu den sogenannten strukturbezogenen Tests: Ihr Ziel ist es, die interne Struktur des betreffenden Softwareelements zu durchleuchten. Diese Art Test erfolgt als White-Box-Test. Dabei sollen der Kontroll und Datenfluss des Objekts überprüft werden. Idealerweise wird eine vollständige Testabdeckung erreicht, d. h., alle Strukturen und Verzweigungen werden mindestens einmal durchlaufen und überprüft.

Da Software i. d. R. ständig an neue Anforderungen angepasst werden muss bzw. Updates bereitgestellt werden, die bekannte Fehler beseitigen sollen, muss auch immer wieder neu getestet werden. Als änderungsbezogene Tests bzw. Regressionstests bezeichnet man Softwaretests, die nach einer Modifikation des Anwendungssystems durchgeführt werden. Dabei soll sichergestellt werden, dass durch die Änderungen keine neuen Fehler in die Anwendung gekommen sind. Diese möglichen neuen Fehler bezeichnet man als Seiteneffekte, und sie sind natürlich unerwünscht. Die Tests umfassen sowohl funktionale als auch nichtfunktionale Aspekte. Je öfter Änderungen des Softwaresystems stattfinden, desto umfangreicher ist auch der Bedarf an solchen Tests. Um die Menge der Tests handhaben zu können, ist das Ziel eine weitgehende Testautomatisierung. Ein Regressionstest kann dabei folgende Teilbereiche umfassen:

  • Test der Funktion, die Anlass für die Programmrevision war
  • Test aller Programmfunktionen, die durch die Programmrevision angepasst wurden
  • Test aller Programmteile, die neu in das Programm aufgenommen wurden
  • Test des kompletten Systems, d. h. ein vollständiger Regressionstest

Nicht nach jeder Programmänderung kann ein vollständiger Testzyklus des gesamten Programmsystems durchgeführt werden; das wäre zu umfangreich. Der ideale Testumfang muss durch Abwägen des Aufwands für einen Test und des Risikos, das verbleibt, wenn bestimmte Tests nicht ausgeführt werden, ermittelt werden.

Die Situation in der Praxis können Sie sich wunderbar anhand von Abbildung 1 verdeutlichen. Dazu sind ein paar Erläuterungen angebracht: Auf der horizontalen x-Achse sehen Sie den Zeitverlauf. Auf der vertikalen y-Achse können Sie den Anteil an getestetem und ungetestetem Code ablesen. Wir befinden uns am Punkt der vertikalen Linie mit der Bezeichnung „Beginne Tests“. Ab diesem Zeitpunkt finden auch Anpassungen an der Software statt. Der Quellcode besteht zu jedem Zeitpunkt aus einem bestimmten Anteil an ungetestetem Code. Wie groß dieser Anteil ist, ist nicht immer bekannt. Das ist zum Beispiel der Fall, wenn Sie als Entwickler ein bestehendes Projekt übernehmen und dieser Fakt nicht aus der Dokumentation hervorgeht. Änderungen am Softwareprojekt, d. h. am Quellcode, finden auf zwei Ebenen statt: Zum einen kann das Projekt erweitert werden, d. h., es wird neuer Programmcode
hinzugefügt. Dieser neue Programmcode wird natürlich bestmöglich getestet, sodass sich der Anteil von ungetestetem Code relativ betrachtet verringert. Zum anderen werden bestimmte Programmfunktionen auch angepasst bzw. geändert. Diese Programmänderungen werden ebenso getestet. Dieses Vorgehen ist praxisrelevant, da man auf diese Weise bestehende Programme erweitern bzw. anpassen kann und gleichzeitig den Abdeckungsgrad des Quelltexts durch Tests sukzessive erhöht. Altanwendungen können dadurch bei notwendigen Anpassungen in der Qualität erhöht und für den weiteren Betrieb bzw. die Pflege fit gemacht werden. Die Aufwendungen für die Tests können wir gegenüber unseren Auftraggebern auch leichter rechtfertigen.

Besonderes Augenmerk verdient die testgetriebene Entwicklung (Test-driven Development, TDD). Hier werden bekanntermaßen die Tests dazu genutzt, die Softwareentwicklung zu steuern. Den Ablauf der Programmierung können Sie sich daher wie in Abbildung 2 dargestellt vorstellen. Es werden drei Zyklen durchlaufen:

  1. Ein Test wird geschrieben und schlägt zunächst fehl (rot).
  2. Es wird genau so viel Produktivcode implementiert, dass der Test erfolgreich durchläuft (grün).
  3. Test und Produktivcode werden refaktorisiert, d. h. überarbeitet.
Abb. 2: Das Prinzip der testgetriebenen Entwicklung

Abb. 2: Das Prinzip der testgetriebenen Entwicklung

Die testgetriebene Entwicklung ist inkrementell, d. h., jeder Zyklus erweitert die Software um neue Fähigkeiten. Dabei soll sehr kleinteilig vorgegangen werden; ein Zyklus sollte nur ein paar Minuten dauern. Herausragend an dieser Vorgehensweise ist, dass die Tests geschrieben werden, bevor die Komponente implementiert wird. Man bezeichnet dieses Vorgehen auch als „Test First“. Der Entwickler bekommt direkt während der Entwicklung das Feedback. Tests und Produktivcode können durchaus von unterschiedlichen Personen geschrieben werden. Damit vermeidet man den eigenen Interessenskonflikt, schreibt einen Test, ohne auf die Implementierung zu achten, und produziert nur so viel Produktivcode, wie wirklich notwendig ist. Beim Refactoring werden die Tests und der Produktivcode gleichermaßen aufgeräumt. Ziel hierbei ist, die Software einfach, redundanzfrei und verständlich zu gestalten. TDD ist ein Kernelement der agilen Softwareentwicklung und soll einen großen Beitrag zu einer qualitativ hochwertigeren und fehlerfreieren Software liefern. Die Vorzüge dieses Vorgehens liegen unmittelbar auf der Hand:

  • kein ungetesteter Code
  • eine saubere und testbare Architektur durch TDD als Designstrategie
  • keine bzw. wenig Redundanzen durch stetiges und rechtzeitiges Refaktorisieren
  • kein unnötiger Produktivcode, d. h., es wird nur immer so viel Code generiert, wie zur Erfüllung des Tests notwendig ist
  • Konzentration auf den Kern

Gelegentlich hören wir, dass die testgetriebene Softwareentwicklung insgesamt zu aufwendig ist. Der Grund ist, dass gewissermaßen jedes Problem zweimal angefasst werden muss, d. h. einmal im Test und einmal beim Schreiben des Produktivcodes. Dieses Argument lässt sich jedoch sehr schnell entkräften, da Software auf jeden Fall nahezu flächendeckend getestet werden sollte und bei dieser Vorgehensweise gerade Einsparungen bei unnötigem Code erfolgen. Das Hauptziel bleibt jedoch die größere Fehlerfreiheit und damit eine höhere Softwarequalität.

Umgang mit Fehlern bei agiler Entwicklung

In den meisten Entwicklungsprojekten wird heute mehr oder weniger agil vorgegangen. Oft wird Scrum als Projektmanagementmethode angewendet. Für eine Darstellung des Ansatzes verweisen wir auf die umfangreiche Literatur zum Thema. Uns interessiert an dieser Stelle lediglich die Retrospektive in Projekten, die mittels Scrum geplant werden. Die Retrospektiven sind ein wichtiger Bestandteil der Projektarbeit – sie dienen dazu, den vorangegangenen Sprint zu analysieren, um sowohl aus Fehlern als auch aus positiven Geschehnissen zu lernen. Am Ende ist man immer schlauer als am Anfang. Diese Erkenntnis versucht man sich in Scrum zum Vorteil zu machen, weswegen das Reflektieren über die letzten Arbeitsschritte und die damit erzielten Ergebnisse einen hohen Stellenwert genießt. Dazu dient ein Meeting am Ende eines Sprints, in dem von allen Mitgliedern des Teams die wichtigsten Fakten ausgewertet werden. Das Verbinden von Theorie und Praxis steht im Mittelpunkt. Die Theorie zur Durchführung einer solchen gemeinsamen Veranstaltung klingt gut. Die Umsetzung scheitert in der Praxis oftmals am herrschenden Zeitdruck und am fehlenden Budget für diese Treffen. Gelegentlich fehlt auch die notwendige Einsicht. Schließlich kostet ein solches Review wertvolle Zeit; daher vertreten manche Beteiligte die Auffassung, dass man besser etwas anderes erledigen könnte. Eine retrospektive Betrachtung, bei der sich die Entwickler gemeinsam über die Erfahrungen und die neu erlangten Erkenntnisse des vergangenen Sprints austauschen, ist wichtig und sorgt für neue Synergien im Team. Dennoch empfinden viele Teammitglieder Retrospektiven als lästige Pflicht, deren Erfolg ihnen zweifelhaft erscheint. In der Tat gelingt es vielen Teams nicht, wirklichen Nutzen aus Retrospektiven zu ziehen. Sie verlieren dann die Motivation für ihre Durchführung und führen sie schließlich nur noch sporadisch durch oder verzichten darauf, um die Zeit für „Produktiveres“ zu nutzen.

Woran liegt das? Was will Scrum mit Retrospektiven erreichen? Im Gegensatz zu traditionellen Entwicklungsprozessen, die versuchen, den gesamten Projektablauf zu definieren, bleibt Scrum offen und beschreibt nur die Rahmenbedingungen. Dadurch muss der Entwicklungsprozess fortlaufend an die Bedürfnisse angepasst werden. Diese Anpassung soll mit der Durchführung von Retrospektiven gelebt werden. Hier ist Raum für die notwendigen Reflektionen. Es werden Verbesserungen für die laufenden Prozesse und das Produkt gefunden. Die Retrospektiven sind also der Schlüssel zum Erfolg, sie sollten daher nicht entfallen. Für eine erfolgreiche Durchführung muss der Scrum Master ein Gespür für das Zwischenmenschliche und die Visionen im Team haben und die Grundprinzipien der agilen Methoden, wie zum Beispiel Selbstorganisation, konsequent anwenden. Nutzen Sie das Feedback Ihrer Teammitglieder. Haben Sie keine Angst vor Kritik – in jedem Fehler finden Sie Potenzial für die Verbesserung.

Wie oft sollte eine Retrospektive durchgeführt werden? Die meisten erfolgreichen agilen Teams führen nach jeder Iteration, also alle zwei bis vier Wochen, Retrospektiven durch. Zusätzlich ist es aber oft hilfreich, auch in einem größeren Zeitabstand längere Retrospektiven durchzuführen, zum Beispiel alle drei bis sechs Monate. Die Durchführung umfasst die folgenden Phasen:

  1. Einführung: Welche Ziele sollen in der heutigen Retrospektive erreicht werden?
  2. Daten aufnehmen: Welche Aktionen waren in der letzten Zeit gut? Welche Ereignisse sind unbedingt zu verbessern? Können wir diese Aussagen anhand von Daten belegen? Nicht alles kann besprochen werden. Entscheiden Sie sich für die dringendsten Themen und erstellen Sie eine Prioritätenliste.
  3. Analyse durchführen: In dieser Phase geht es darum, zu erforschen, warum bestimmte Sachverhalte eingetreten sind. Man betreibt Ursachenforschung. Nur wenn die Ursachen bekannt sind, kann man zielführende und verbessernde Maßnahmen beschließen.
  4. Konkrete Maßnahmen beschließen: Welche Schritte sollen durchlaufen werden, um die o. g. Kritikpunkte in der Zukunft zu verbessern?
  5. Schluss: Reflektieren Sie die eben durchgeführte Retrospektive. Ist die Zeit sinnvoll investiert? Nur dann werden die Teilnehmer auch in der Zukunft Lust und Interesse haben, weitere Retrospektiven durchzuführen.

Wichtig: Eine Retrospektive ist kein „Kaffeekränzchen“, d. h., sie braucht eine genaue zeitliche Begrenzung. Je nach behandelten Themen, betrachteten Zeitintervallen und der Häufigkeit der Durchführung sollten Sie sich unbedingt klare zeitliche Limits setzen, zum Beispiel:

  • bei Sprints mit einer Länge von vier Wochen eine Timebox von drei Stunden;
  • bei einem Sprint von zwei Wochen 1,5 Stunden;
  • bei einer Sprintdauer von einer Woche knapp vierzig Minuten.

Die Teilnehmer müssen den Nutzen einer Retrospektive im Verhältnis zum Aufwand als positiv einschätzen. Zusammengefasst: Agile Methoden wie Scrum tragen dem Umstand Rechnung, dass Softwareentwicklung ohne Fehler nicht möglich ist. Die feste Verankerung von Retrospektiven in die Arbeitsabläufe soll die Qualität der laufenden und vor allem der kommenden Sprints verbessern. Wir lernen sozusagen aus den Fehlern – aber das geht nur dann, wenn die Fehler offen kommuniziert werden und man bereit ist, die Fehler gemeinsam im Team zu diskutieren. Dieser Weg des kontinuierlichen Verbesserungsprozesses (KVP) kann aber nur dann gelingen, wenn Sie in Ihrem Scrum-Team eine faire Arbeitsweise implementieren, d. h. beispielsweise, dass die geäußerte Kritik dem gemeinsamen Fortschritt dienen muss und nicht zur Schuldzuweisung an einzelne Personen werden darf.

Fehlerkultur

Bisher wurden vielfältige technische Aspekte zum Vermeiden, Entdecken und Beseitigen von Fehlern aufgezeigt. Damit die Mitarbeiter aktiv gegen Fehler vorgehen und langfristig ein Lerneffekt aus den gemachten Fehlern entsteht, muss man die eingangs beschriebenen, negativen Einstellungen gegenüber Fehlern in eine möglichst positive Fehlerkultur im Unternehmen wandeln. Bestandteile einer solchen positiven Fehlerkultur sind folgende Merkmale (mediapp.ch, t3n.de):

  • Fehler eingestehen: Fehler sind Bestandteil der Arbeit. Sie gehören dazu.
  • Fehler nicht sanktionieren: Strafen führen nicht zu einem Lerneffekt. Es findet kein Transfer von Wissen statt, denn alle wollen nur einen eigenen Nachteil vermeiden.
  • Aus Fehlern lernen: Nur dann kann man erreichen, einen Fehler nicht zweimal zu machen.
  • Fehler sollen nicht blockieren: Mögliche Fehler und ihre Folgen dürfen nicht die Experimentierfreude und Innovationslust bremsen.
  • Fehler finden ist positiv: Jeder Fehler, den wir als Entwickler finden, tritt nicht beim Nutzer ein.
  • Fehler vom Verursacher beheben lassen: Dieser steckt am besten in den Prozessen. Der zuständige Entwickler kennt den Code am besten. Ebenso motiviert es langfristig zu einer höheren Qualität der Codierung. Bestehenden Code von Fehlern zu befreien, macht weniger Spaß als neue Features zu programmieren (eigener Lerneffekt).
  • Fehler kommunizieren: Nur dann können andere aus unseren Fehlern lernen (Lerneffekt im Team).

Kultur und damit auch Fehlerkultur meint das nicht Sichtbare in einem Unternehmen. Das hat zur Folge, dass Sie den Weg zu einer besseren Fehlerkultur im Team auch nicht vorschreiben können. Diese muss unbedingt vom Management vorgelebt werden.

Fazit

Software ohne Fehler bleibt wohl weiterhin ein Traum. Mit einer gesunden Fehlerkultur und einem umfassenden Testmanagement gelingt es jedoch, viele Fehler von unseren Kunden fernzuhalten. Im Übrigen sind Produktnachbesserungen in vielen Branchen – in der Softwareentwicklung in Form von Updates – mittlerweile auch durch die Nutzer akzeptiert. Der Nutzer weiß, dass es keine vollständig fehlerfreien Anwendungen gibt.

Veikko Krypczyk

Dr. Veikko Krypczyk ist begeisterter Entwickler und Fachautor. Er ist stets auf der Suche nach neuen innovativen Lösungen und liebt die Arbeit in der Entwicklungsumgebung. Er bietet Seminare und Workshops zu unterschiedlichen Themen der Softwareentwicklung.

Dr. Veikko Krypczyk studierte Betriebswirtschaftslehrte und promovierte in der Fachrichtung Wirtschaftsinformatik. Er arbeitet als Softwareentwickler, Fachautor und Berater in Fragen rund um die IT. Seine Arbeitsschwerpunkte sind Desktopapplikationen und die 2-D-Grafikprogrammierung. Vielfältige Informationen können auf http://it-fachartikel.de nachgelesen werden. Ein Gedankenaustausch ist unter info@it-fachartikel.de ausdrücklich erwünscht.