Auf dem Kreuzzug gegen Fehler (in Software)

September 4, 2008

“Auf dem Kreuzzug gegen Fehler in Software” ist eine Präsentation die ich am 4.9.2008 für die bbv Software Services AG gehalten habe. Hier ist die Präsentation als Artikel Form - für all diejenigen die nicht an die Präsentation kommen konnten.

Der erste Teil behandelt einige Dinge die auf den ersten Blick normal und richtig erscheinen - aber beim näheren hinschauen Fehler garantieren.

Problem 1: Software wird getestet wenn sie \

Software wird getestet, wenn sie fertig ist. Das Problem dabei ist die Interpretation von “Fertig”. Wann ist die Software soweit, dass sie getestet werden kann. Das folgende ist die Interpretation der Entwickler und zeigt warum Software oft mit sehr vielen Fehlern in die “Testphase” geht.

Versteckte Arbeit in der Form von Fehlern

Auf der Y-Koordinate haben wir den Prozentgrad der Fertigstellung der Software. Auf der X-Achse ist die Zeit die vergeht und die Deadline, die gesetzt ist. Die Entwickler machen stetigen Fortschritt und merken dann nach etwa zwei Drittel dass es knapp wird. An diesem Punkt beginnen sie härter, schlauer und mehr zu arbeiten und werden genau auf die Deadline fertig. “YEAH!” - Zeit für die Entwickler bei einem Apero anzustossen und sich gegenseitig auf die Schulter zu klopfen.

Was allerdings passiert ist, dass neben der eigentlichen Arbeit noch zusätzliche Arbeit gemacht wird. Versteckte Arbeit. Oder Negative Arbeit. Je näher wir zur Deadline kommen, desto mehr unentdeckte Fehler sind in der Software.

Fertig heisst für die Entwickler eigentlich nur: “Wir haben alle Features implementiert und jedes Features hat mindestens einmal Fehlerfrei (zumindest fast) funktioniert.”

Je später desto mehr codieren und desto weniger Anforderungen verstehen

Hier ein verwandtes Problem: Die Y-Achse zeigt an wie viel Zeit wir für eine Arbeit aufwenden. Auf der X-Achse haben wir wieder die vergehende Zeit. Die erste Linie ist: Wie viel Zeit verbringen wir mit dem eigentlichen erstellen der Software (Coding). In der Phase 1 wird wenig gemacht - die Entwickler versuchen die Business Domäne zu verstehen, schauen sich Frameworks an etc. Je näher die Deadline rückt, desto mehr fokussieren sie sich auf das erstellen der Features.

Auf der anderen Seite kümmern sich die Entwickler am Anfang sehr stark um das korrekte Verständnis der Anforderungen. Je näher die Deadline kommt, desto weniger arbeiten sie daran.

Auf den ersten Blick scheint das Sinn zu machen. Wenn man die Anforderungen einmal verstanden hat, dann muss man es ja nur noch umsetzen.

Das trügt leider.

In Phase 1 verbringen die Entwickler viel Zeit bei Diskussionen mit den Leuten die die Anforderungen gestellt haben und passen die nötigen Dokumente an. In dieser Zeit werden praktisch keine Fehler erstellt - es wird ja auch nur wenig Code erstellt.

In Phase 2 beginnen die Entwickler mit der “richtigen” Arbeit - sie haben die Anforderungen einigermassen Verstanden. Programmieren ist allerdings eine Detailarbeit - es kommen immer wieder Fälle vor, die nicht klar sind. Diese Fälle werden mit dem Kunden besprochen und meistens ins Anforderungsdokument aufgenommen.

In Phase 3 wird es langsam zeitkritisch. Die Deadline naht. Die Entwickler kennen die Domäne des Kunden schon recht gut. Sie haben jetzt keine Zeit mehr für Fragen (vor allem wenn der Kunde nicht grad “griffbereit” ist) und sie entscheiden z. T. selber wie sie die Anforderungen interpretieren. Wenn zwei Interpretationen etwa gleich wahrscheinlich sind, macht es aus logischer Sicht Sinn diejenige zu wählen die weniger Arbeit gibt. Das Anforderungsdokument wird nicht mehr angepasst.

Das bedeutet dann häufig, dass das Anforderungsdokument nicht mehr mit der Software übereinstimmt. Es bedeutet auch, dass in der Testphase viele Fehler gefunden werden, die eigentlich gar keine sind. Und dass Fehler NICHT gefunden werden, weil der Tester die Interpretation des Entwicklers versteht und ihm “Recht” gibt, dass das Verhalten der Software so spezifiziert ist.

Software wird nicht testbar designed

Nehmen Sie sich einmal die Mühe und fragen sie einen Entwickler wie die Applikation aufgebaut ist. Mit grösster Wahrscheinlichkeit wird er Aussagen machen wie: “Der Business Layer benutzt dann den Persistence Layer. Den Persistence Layer haben wir um die Datenbankunabhängigkeit zu gewährleisten. Und den Business Layer können wir auf verschiedenen Maschinen deployen und so skalieren.”

Die Software wird für alles designed! Wechsel von Oracle auf XML Files? Kein Problem! Wechsel von Windows auf Linux? Kein Problem! Anstelle der Logfiles sollen die Logeinträge an einen Webservice geschickt werden? Piece of cake!

Nachdem Sie dann den Entwickler entsprechend gelobt haben, fragen Sie ihn einmal welche Konzepte er eingebaut hat, dass die Applikation “testbar” wird. Kann ich jede Bildschirm Maske innerhalb von 3s öffnen und einstellen, in welchem Zustand die angezeigten Daten sind, damit ich das UI Verhalten testen kann? Wie erstelle ich Testdaten? etc.

Die Antwort ist leider meistens sehr kurz und kann in Null Worten zusammengefasst werden.

Mit anderen Worten: Wir Entwickler designen die Software für alle Eventualitäten - aber wir designen sie nicht, damit sie einfach zu testen ist. Sorry - aber wir dachten das Testen sei nicht unser Problem…

Missbrauch des Begriffs \

Bugfix Iterationen und Flexible Iterationen kommen häufig in Firmen vor die denken “agile” Entwicklung zu machen. Beides sind aber eigentlich “Agile Anti-Patterns” - wer das macht, hat die agilen Entwicklungsmethoden noch nicht ganz verstanden (Ausnahmen bestätigen die Regel und die Chance dass Ihre Firma eine Ausnahme ist, ist klein).

Bugfix Iteration

Die Bugfix Iteration kommt folgendermassen zu Stande: Die Entwickler programmieren eine Zeit friedlich vor sich her und erstellen mehr und mehr Features. Sie generieren auch Fehler, die nicht sofort entdeckt werden. Die versteckte Arbeit halt. Beim ersten “x” geht die Software zum ersten Mal an die Testabteilung über. Die Entwickler entwickeln weiter, kommen aber nicht mehr ganz so schnell voran, weil sie den Testern helfen müssen mit Infos wie: “Wie kann ich xyz testen?” “Wie setze ich die DB neu auf?” “Warum kann ich die Applikation jetzt nicht mehr starten?”

Am Ende dieser Iteration hat die Testabteilung 50 Fehler gefunden. Was passiert jetzt? Die Fehler müssen behoben werden - es wird eine Bugfix “Iteration” eingeschoben. Für die Projektleitung ist eine Bugfix Iteration eine Iteration die keinen Mehrwert generiert. Die Software kann nachher nicht mehr als sie vorher schon hätte tun können sollen (hätte können sollen? gekonnt haben sollte? OK, dieser Satz ist nicht der Glanzpunkt dieses Artikels).

Flexible Iterationen sind schwarze Schimmel

Die Worte Flexibel und Iteration sollten nie nacheinander kommen. Iterationen ist ein fixer Zeitraum. Der wird nicht ausgedehnt. Wenn man sagt 2 Wochen, dann sind es nicht plötzlich 3 oder 4. Das ist einer der wichtigen Punkte der agilen Entwicklungsmethoden. Nach 2 Wochen ist man fertig. Eventuell hat man NICHT ALLES fertig. Aber man kann den Fortschritt (oder die Absenz davon) zeigen und festlegen wies weiter gehen soll.

Nach 1.8 Wochen feststellen, dass man noch viele Fehler hat und nicht fertig wird und deshalb die Iteration um 1 Woche ausdehnt - das ist vieles - aber ganz sicher kein agiles Vorgehen.

Bugzilla ist kein Kommunikationstool zwischen Entwicklern und Testern

Ich will die Tools nicht generell als schlechtes Zeichen interpretieren. Aber wenn es als Kommunikationsmittel zwischen den Entwicklern und den Testern eingesetzt wird ist das völlig falsch! Wie viele Fehler sollen wir in die Software programmieren? Maximum 5? Wozu brauchen wir da ein Bugzilla? Wenn es natürlich nicht darauf ankommt wie viele Bugs wir erstellen und es wichtig ist so schnell wie möglich irgendwas zu machen - dann ist Bugzilla super. Rettet aber das Projekt auch nicht…

Bugzilla und Jira und wie sie alle heissen machen in gewissen Umfeldern sicher Sinn. Zum Beispiel für Open Source Projekte oder für die Kommunikation mit vielen Kunden etc.

Diese Verhalten sind...Brutstätten für Bugs

Diese Verhalten - obwohl sie auf den ersten Blick sinnvoll erscheinen - gewährleisten dass die Software voller Fehler ist.

Die Frage ist, können wir diese Verhalten verändern? Und zwar so dass die Wahrscheinlichkeit Fehler einzubauen minimiert wird?

Und die agilen Software Entwicklungsmethoden bringen eine Antwort. Diese Methoden definieren den Ablauf zum erstellen von Software neu (im Vergleich zu Wasserfall) - und dadurch auch die Verhaltensweisen.

Einer der fundamentalen Grundsätze von Agilen Entwicklungsmethoden sind die Iterationen. Ein Konzept das zwar einfach ist, aber praktisch immer falsch angewendet wird. Es ist auch ein Konzept, das es ermöglicht den Bugs das Leben schwer zu machen.

Also schauen wir uns dieses Konzept mal etwas genauer an.

Eigentlich gibt es nur zwei Punkte zur Definition einer Iteration benötigt werden:

1. Timeboxed. Iterationen haben eine bestimmte Länge (z. B. 2 Wochen). Nach 2 Wochen ist die Iteration abgeschlossen. Es gibt keine “Verlängerung”. Die Iterationen geben dem Projekt einen Rythmus - oder Puls. Wenn der Puls einmal ausbleibt, ist das schon ein Grund zur Sorge.

2. Software ist fertig. Das heisst installiert und funktionsfähig. Ein Architekturdokument gilt nicht als lauffähige installierte Software. Test Dokumente auch nicht. Und ein Feature das Fehler hat auch nicht. Fertige Software bedeutet, dass wir am Ende einer Iteration immer feststellen können, wie gut wir vorwärts kommen. Dadurch wird das Projekt steuerbar, weil der Projektfortschritt sichtbar wird.

Hier ist die Entwicklungsmethode “Scrum” - ein bisschen abstrahiert. Auf dem Backlog befinden sich Features die implementiert werden müssen. Die Iteration beginnt mit Planung. Es wird entschlossen, welche Features von der Liste genommen und in den nächsten 2 Wochen implementiert werden. Danach werden diese Features produziert. In dieser Zeit kommen keine “zusätzlichen” Features dazu. Oder andere, weil sich die Prioritäten geändert haben. Das Team ist dafür verantwortlich, dass die Features (und alle vorherigen) am Schluss der Iteration fehlerfrei laufen.

Denn dann wird die Software demonstriert - das heisst sie ist installiert und sie funktioniert (hoffentlich - sonst sieht das Team ein bisschen alt aus an der Demo). Nach der Demo nimmt man sich die Zeit und überlegt: Gibt es Dinge, die wir besser machen können? Gibt es Probleme die auf uns zukommen, wenn wir so weitermachen?

Danach nimmt man die nächsten Features vom Backlog und vervollständigt/erweitert die Software.

Ron Jeffries hat auf der Scrum Mailliste einmal geschildert warum Scrum funktioniert und auf zwei Schritte herunter gebgrochen. 1. Regelmässig FERTIGE Software herstellen. 2. Alle Hindernisse zu Punkt 1 aus dem Weg räumen.

Done-Done Software heisst, dass die Software getestet und fehlerfrei ist. OK, nun die Frage - ist es ein Hindernis wenn wir die Software an eine QA Abteilung übergeben müssen, damit wir wissen ob sie fehlerfrei ist? Ja? Dann siehe Punkt 2!

Auf der ersten Y-Achse ist die Metrik “Running Tested Features”. Auf dieser Achse geht man nach oben, wenn 1 Feature implementiert ist UND all seine Tests erfolgreich besteht. Auf der zweiten Y-Achse ist die Zeit, die für die MANUELLE Testdurchführung gebraucht wird.

Nach 2 Wochen ist die erste Iteration abgeschlossen. Es sind 2 Features implementiert worden, die problemlos am Ende der Iteration manuell getestet werden können. Der Testaufwand ist vielleicht 1 h. Nach der zweiten Iteration sind 2 weitere Features dazu gekommen, die z. T. das Verhalten der ersten etwas beeinflusst. Um die Software zu testen, müssen alle Tests der ersten Iteration durchgeführt werden plus alle neuen. Der Aufwand ist immer noch absehbar, vielleicht 2.5 h.

Aber während die Entwicklung Iteration für Iteration mehr Features produziert, brauchen die Manuellen Tests immer länger (es muss ja immer wieder die ganze Vergangenheit getestet werden). Nach einigen Iterationen brauchen die Manuellen Tests 10 h. Das bedeutet, dass der Code jetzt schon 2 Tage vor Ende der Iteration fertig sein muss. Einige Iterationen später dauern die manuellen Tests 1 Woche. Das heisst die Entwickler haben noch 4 Tage zum entwickeln. Irgendwann brauchen die Manuellen Tests länger als die Dauer der Iteration… Was machen wir dann?

Ich sehe da nur drei mögliche Auswege:

1. Die Entwickler geben sich endlich etwas Mühe und erstellen keine Bugs mehr.
2. Wir testen nicht jede Iteration, sondern nur jeden Release
3. Wir automatisieren die Tests als Bestandteil der Iteration

Punkt 1 funktioniert leider nicht. Das haben wir probiert. Fehler passieren sogar den Entwicklern.
Punkt 2 führt zu dem Problem das wir im ersten Teil der Präsentation gesehen haben. Es werden Fehler erstellt, die spät entdeckt werden. Wir können nicht sagen welche Teile der Software funktionieren und welche nicht.
Das lässt nur noch den 3 Punkt als Ausweg und - man höre und staune - das könnte klappen!

Mit anderen Worten: Automatisierte Tests müssen Bestandteil der Entwicklungsmethode werden. Wenn wir das nicht tun, dann müssen wir einen der beiden anderen Wege begehen - und die funktionieren nicht. Wie binden wir also die zu automatisierenden Tests in den Prozess ein?

Einfach gesagt: als Spezifikation bevor das Codieren beginnt. Aber ich möchte da noch etwas ausholen um zu zeigen warum das Sinn macht.

In einigen Projekten gibts ein Requirements.doc (Anforderungsspezifikation). Wozu brauchen wir das während der Entwicklung? Eigentlich für 3 Sachen. Wir brauchen es um Schätzungen zu machen, um zu lehren wie das Business funktioniert und um zu beweisen, dass die erstellte Software das tut was sie tun soll.

Die meisten agilen Methoden brauchen für diese drei verschiedenen Aspekte auch drei verschiedene Mittel. Für die Planung reichen Feature Beschreibungen. Das erlernen der Business Domäne passiert vorzugsweise über Konversationen und das Beweisen ist am besten durch automatisierte Tests gelöst.

Das bedeutet aber auch, dass die automatisierten Tests nicht kryptisch sein dürfen. Das heisst, ich muss sie meiner Grossmutter geben können und sie muss sie verstehen. Und das ist da, wo ein Word Dokument brilliert. Da es “normale” Worte sind wie sie in jedem Buch zu finden sind, sollte sie auch jeder Verstehen…

Zeit für ein kleines Praxis Beispiel!

Das ist ein Auszug aus der Feature Liste für ein Ausleihsystem an dem ich zur Zeit mitarbeite. Es hat Dinge drauf, die klar vom Business her kommen: Bestellungen bestätigen, Modelle auswählen und bestellen, Verfügbarkeit von Modellen.

Aber auch Dinge, die vom technischen Umfeld her kommen: Authentifizierung über LDAP, Zugriffsberechtigungen.

Im folgenden nehmen wir das Feature “Bestellungen bestätigen” etwas genauer unter die Lupe.

Hier nun der Ablauf, wie wir vom Feature zum Code kommen.

Das Feature haben wir ja bereits ausgewählt. In der Konversation versuchen wir Details zum Feature rauszufinden und schreiben nachher Tests, die unser Verständnis des Features ausdrücken (wie gesagt, der Test muss für jeden verständlich sein). Danach wird der Code geschrieben, damit diese Tests erfolgreich durchlaufen.

Eventuell tauchen bei der Implementation Fragen auf - worauf die Konversation wieder aufgenommen wird, die Frage geklärt und neue Tests geschrieben werden. Der Code wird angepasst oder erweitert. So wachsen unsere Requirements, die automatisierten Tests und der Code gleichzeitig. Die Software wird immer vollständiger.

Hier ein Beispiel eines automatisierten Tests. Es geht um das Feature “Bestellung bestätigen”. Wir haben eine kurze Beschreibung die vor allem erklärt, wer das Feature will und wozu es gebraucht wird.

Jedes Feature hat verschiedene Szenarien (nichts anderes als Tests), mit einem eigenen Namen. Hier ein ganz simples: “Die Liste mit neu erstellten Bestellungen”

Dann die Testbeschreibung.

Dort wird die Ausgangslage festgelegt: Die Liste der neuen Bestellungen ist leer und ein User mit dem Namen ‘Joe’ macht eine neue Bestellung.
Aktionen werden ausgeführt: Der Lagerverwalter klickt auf “bestätigen”
Und festgelegt, was dann passiert: Der Lagerverwalter sieht eine Bestellung und dass sie vom User ‘Joe’ erstellt wurde.

Hier ein etwas komplizierteres Beispiel.

Joe hat eine Bestellung gemacht, der Lagerverwalter wählt diese Bestellung aus.

Es geht ein neuer Tab auf mit dem Namen ‘Order: Joe’ und der Verwalter hat die Möglichkeiten zu bestätigen oder Abzulehnen

Er bestätigt, was dazu führt dass Joe ein Email mit der ‘Reservation Confirmation’ erhält.

Die Tests die wir gesehen haben sind für ein Programm sehr einfach zu parsen und auf die entsprechende Business Logik zu mappen. Die Tests sind aber auch für jeden mit nur wenig Aufwand verständlich.

Fertig im agilen Sinne heisst also: Wir haben ein Feature, das durch X Tests definiert wird. Wenn ALLE Tests erfolgreich durchlaufen, ist das Feature vollständig implementiert.

Hmmm. Das tönt ja alle recht interessant und spannend, aber warum ist das wichtig. Oder vielmehr: was bedeutet das für die Testabteilung.

Naja, wenn wir die automatisierten Tests während der Entwicklung machen und wir die Testphase abschaffen. Was passiert dann mit den Testern?

Ganz offensichtlich verändert dies den Job der Tester sehr stark! Keine Angst, es verändert auch den Job des Entwicklers sehr stark! Und denjenigen des Kunden! Viele Entwickler haben sich schon verändert und es macht ihnen mehr Spass. Ich glaube für die Tester ist das ebenfalls eine Riesenchance sich neu zu platzieren und neue spannende Aufgaben zu finden. Der Agile Tester ist ein ganz anderer Job als der Wasserfall Tester!

Ich will das nochmals in aller Deutlichkeit sagen: Es braucht nicht keine Tester mehr.

Aber es heisst, dass die Tester und Entwickler im gleichen Team über das ganze Projekt zusammenarbeiten müssen. Der Tester muss jedes Feature verstehen - genau wie die Entwickler auch. Der Tester teilt die Verantwortlichkeit mit allen anderen Teammitgliedern, dass am Ende jeder Iteration releasebare Software rauskommt.

Eventuell übernimmt er sogar einige der folgenden Aufgaben. Das sind alles Aufgaben, bei denen die Zuständigkeit noch nicht so klar ist. Agile Methoden legen viel Wert auf die Leute die mitarbeiten (People over Process etc.). Jeder hat seine eigenen individuellen Fähigkeiten die er in ein agiles Team bringen kann - die folgenden Aufgaben sollen lediglich als Startpunkt dienen. Am Schluss sollte sich jeder selber überlegen welche Aufgaben ihn interessieren und wo er dem Team am meisten helfen kann.

Wenn der Kunde es nicht fertig bringt die Tests so zu schreiben, dass sie für Menschen und Computer lesbar sind, dann muss da jemand helfen. Entweder als Mentor oder als Übersetzer. Wenn man das den Entwicklern überlässt, schreiben sie die Tests selber und das vermindert den Buy-In des Kunden.

Viele Entwickler kennen ihr Unit-Testtool (JUnit) und damit hat sichs. Testtool impliziert für Entwickler, dass es sich da um ein Gebiet handelt, das sie gar nicht zu genau kennen wollen. Es braucht jemand der weiss, welches Tool wann und wie sinnvoll eingesetzt werden kann.

Es gibt sicher auch Tools, die im agilen Umfeld keinen Sinn machen. Eine Person die diese Tools kennt und sagen kann dass man damit keine Zeit verschwenden muss, ist natürlich viel Wert.

Für Kunden ist es manchmal schwierig ein Feature in kleinere Features zu unterteilen, von denen jedes einzeln getestet werden kann. Kleinere Teile können einfacher getestet werden. Idealerweise können kleinere Features rausgelöst werden, die den grössten Teil des Business Values haben. So kann dem Kunden sogar geholfen werden, schneller eine Software auf die Beine zu stellen, die Wert generiert.

Kunden denken hauptsächlich darüber nach, was sie wollen. Weniger über das, was sie nicht wollen. Stress Tests, Negativ Tests, Security etc. sollten immer auch mit einbezogen werden.

Ich war noch nie in einem Projekt, in dem die Testdaten gut und einfach gelöst waren. Wie bekomme ich das System in den richtigen Zustand um genau DAS zu testen? Wie stelle ich diese Daten den automatisierten Tests zur Verfügung? Wie den Entwicklern, die ihre Änderungen ausprobieren wollen? Wie können wir sie für die Demo verwenden?

Eventuell hat jemand von Euch da Antworten drauf und ich bin sicher dass die Entwickler dieses Wissen mit offenen Armen begrüssen würden.

Direkt = Fehler werden verhindert. Nicht wie “früher”, wo der Tester lediglich da war um Bugs zu finden. Hat ein Tester ein gutes Gefühl wenn er einen Bug findet? Oder hat er ein gutes Gefühl, wenn die Software richtig funktioniert? Das Erfolgserlebnis sollte das zweite sein.

Es reicht nicht, wenn wir versuchen die Bugs mit einer Testphase zu erschrecken und zu hoffen dass sie verschwinden. Wir müssen die Art wie wir Software entwickeln umstellen. Und wenn ich “Software entwickeln” sage, dann ist das Testen mit dabei. Tester die bugfreie Software wollen, setzen sich dafür ein, dass die Testphase abgeschafft wird.

Viele Betriebe wollen in naher Zukunft auf Agile Entwicklungsmethoden umsteigen. Ich hoffe ich konnte ihnen zeigen, dass diese Idee gut ist. Und ich hoffe ich konnte ihnen ein paar Gedankenanstösse geben, damit sie, wenn es dann soweit ist, schon einen kleinen Vorsprung haben.

Posted by Jerome Mueller

5 Comments

  1. Stavros Antifakos says:


    war echt guter Vortrag, guter Auftritt.
    congrats.

    Grüsse aus Luzern

  2. Jerome Mueller says:


    Vielen Dank, das höre ich natürlich gerne ;-)

  3. Linda Berthold says:


    jepp - ich hab’s gelesen - Ganz. Und ich muss eins loswerden:
    wow - der Mann ist gut!
    Ich wünsch dir ganz viel Glück für die Zukunft!

  4. Tamy Wüller says:


    Heftig, ich hätte never ever für möglich gehalten, dass das wirklich auch so umsetzbar ist ;)

  5. Pakie Postof says:


    Da fragt man sich beim groben Lesen ja schon, ob man selbst nicht komplett auf den Kopf gefallen ist. Dankeschon fur deine Erklarungen

Leave a Reply