Is TDD dead?
Im Verlauf der letzten vier Wochen wurde die Entwicklergemeinde Zeuge einer Reihe von interessanten Konversationen zwischen Kent Beck, Martin Fowler und David Heinemeier Hansson zu einem Thema, das seit vielen Jahren sehr kontrovers und leider oft auch sehr emotional und auf niedrigem inhaltlichen und teilweise zweifelhaftem zwischenmenschlichem Niveau diskutiert wird: TDD – Test Driven Development. Die Gespräche sind als Video und Audio verfügbar und ich kann sie sehr empfehlen.
Mich selbst hat die Debatte inspiriert, mich mit drei unrealistischen Vorstellungen über die Wirkung von TDD zu beschäftigen. Alle drei spielen in den Diskussionen um TDD oft eine negative Rolle. Beginnen wir also mit der Entmystifizierung der populärsten falschen Ideen über Test Driven Development!
Erster Mythos: Mit TDD findet man eine Lösung zu jedem erdenklichen Problem
Mit anderen Worten: TDD ist die Wunderwaffe der Softwareentwicklung! Egal, wie schwer die Aufgabe ist und wie viele schlaue Menschen an der Lösung verzweifelt sind: durch testgetriebene Entwicklung ist die Lösung immer erreichbar! Diese Idee erfreut sich dank des Versprechens der universellen Problembewältigung großer Beliebtheit, obwohl sie offensichtlich von Wunschdenken geprägt ist. Den öffentlichen Beweis dafür lieferte vor einigen Jahren Ron Jeffries: Er versuchte ein Programm zu entwickeln, das Sudoku-Rätsel löst. In mehreren Iterationen (Teil 1, Teil 2, Teil 3, Teil 4 und Teil 5) produzierte er dabei zwar einiges an Code und beschäftigte sich eingehend mit der Modellierung des Spielfelds, kam jedoch nie auch nur in die Nähe seines Ziels. Sein Vorgehen wurde mit dem von Peter Norvig verglichen: Dieser stellte in einem Blogpost eine kurze und elegante Lösung dar, ohne sich auf TDD zu beziehen.
Der Unterschied zwischen beiden Programmen ist augenscheinlich: Norvig kannte zwei Techniken aus dem Bereich der künstlichen Intelligenz, die das Problem lösen – Jeffries nicht. Stattdessen konzentrierte sich die Diskussion aber darauf, dass Jeffries testgetrieben vorging und Norvig nicht – nach dem Motto Jeffries konnte das Problem nicht lösen, da er testgetrieben vorging. Ebenso wahrscheinliche Erklärungen für den unterschiedlichen Erfolg wären: Norvig lebt im sonnigen Kalifornien, Jeffries dagegen in Michigan – sollte es vielleicht an der Sonneneinstrahlung liegen? Verschiedene Ernährungsgewohnheiten oder Sprachvorlieben? Die Möglichkeiten sind endlos und die einfachste und wahrscheinlichste Erklärung ist: Wenn man nicht weiß, wie man ein Problem löst, dann kann man auch keine Lösung implementieren.
Zweiter Mythos: Dank TDD brauchen wir weder andere Tests noch (menschliche) Tester!
Erinnern Sie sich noch an den legendären Elchtest des damaligen A-Klasse-Modells von Mercedes? In einem Fahrdynamiktest kippte der Wagen 1997 auf die Seite und brachte so den Hersteller in Erklärungsnot. Man kann sich sicher sein, dass die Ingenieure in der Entwicklung des Wagens alle Komponenten eingehend getestet haben – trotzdem war das Gesamtprodukt fehlerhaft.
Wenn man Software- und Automobilentwicklung vergleicht, dann findet man Parallelen zwischen einen Unittest und dem Test eines Bauteils – wie zum Beispiel Batterie, Anlasser oder Bremse. Diese Komponenten werden natürlich zunächst für sich getestet, um ihre korrekte Funktionsweise zu prüfen. Kein Ingenieur der Welt würde jedoch auf die Idee kommen ein Auto, dessen Komponenten sehr ausführlich getestet wurden, nach der Montage einfach so auszuliefern. Stattdessen wird das Gesamtprodukt noch den verschiedensten manuellen und automatischen Tests unterworfen. Und selbst dann passiert es hin und wieder, das kleinere Fehler nicht erkannt werden (siehe Elchtest). Diese Tests sind also unumgänglich, wenn man eine niedrige Fehlerrate erreichen möchte. So falsch die Aussage ist, dass man keine anderen Tests als die durch TDD erzeugten brauchen würde, so falsch ist allerdings auch die Gegenargumentation: Wenn ich Tests des Gesamtsystems mache, wozu dann noch Unittests?
Auch diese Frage kann man mit der Parallele zur Automobilentwicklung einfach beantworten: Nehmen wir an, wir würden einen Wagen aus ungetesteten Komponenten zusammenbauen. Am Ende versuchen wir den Wagen zu starten und haben keinen Erfolg. Da wir nicht wissen, ob die einzelnen Komponenten funktionieren oder nicht, müssen wir nun nicht nur die Interaktion der einzelnen Bauteile überprüfen, sondern auch die einzelnen Teile selbst. Es reicht also nicht, nur zu kontrollieren, ob beispielsweise eine Steckverbindung lose ist, sondern wir müssen auch den Anlasser ausbauen und testen. Genauso verhält es sich bei der Softwareentwicklung. Je weniger Teile der Software auf Korrektheit geprüft sind, desto weniger Fehlerquellen können ausgeschlossen werden: Mit anderen Worten verbringt man (zu) viel Zeit mit dem Debugger. Tests auf verschiedenen Ebenen ergänzen sich also und machen sich nicht gegenseitig überflüssig.
Menschliche Tester sind durch TDD ebensowenig zu ersetzen. In meinem letzen Projekt hatte unser Team es sich zur Regel gemacht, dass abgeschlossene User Stories immer von anderen Teammitgliedern überprüft wurden als denjenigen, die an der Umsetzung maßgeblich beteiligt waren. Mit wenigen Ausnahmen fanden wir immer Aspekte, die zu verbessern waren. Unterbewusst wollte nämlich jeder von uns, dass seine Arbeit fehlerlos war – was sie natürlich in den seltensten Fällen ist. Wir waren betriebsblind und die Überprüfung durch einen Unbeteiligten in allen Fällen den Aufwand wert. Darauf zu verzichten, ist keine gute Idee.
Dritter Mythos: TDD führt automatisch zu einer angemessenen Softwarearchitektur
Braucht es also keinerlei Überlegungen in Sachen Architektur oder Design einer Software, sondern findet sich diese durch ständiges Refactoring nach und nach von selbst? Das ist eine verlockende Idee, denn Softwarearchitektur zu entwerfen ist schließlich eine schwierige Aufgabe. Das Problem dabei ist, dass sich TDD auf einer feingranularen Ebene abspielt: Es geht um einzelne Methoden, Klassen oder Funktionen. Natürlich setzt sich jede Software aus diesen Komponenten zusammen, genauso wie eine Mauer aus einzelnen Steinen besteht. Aber trotzdem ist es wert, sich darüber Gedanken zu machen, wie das Bauwerk bzw. die Software letztendlich aussehen sollte. TDD beeinflusst sicherlich das Design der einzelnen Units und das, wenn man es richtig macht, auch auf positive Weise. Auf grobgranularer Ebene jedoch wird das Produkt dann immer zufällig sein – was keine gute Idee ist.
Woher kommen diese Mythen?
Es ist schwierig zu sagen, was diese Mythen populär gemacht hat oder die Motivation für solche Aussagen zu erraten. Verkaufen sich Seminare, Bücher oder Coachings besser, wenn man TDD als Allheilmittel, als „Silver Bullet“ verkauft? Sind einige so begeistert von der Technik, dass sie ihr zu viele Vorteile zubilligen? Gibt es Entwickler, die durch die mangelnde Adaption von TDD frustriert sind und daher versuchen sie aggressiv zu verkaufen? Versuchen Gegner durch solche Behauptungen, die Methode in Verruf zu bringen?
Sicher ist dagegen, dass diese Ideen der weiteren Adaption von TDD schaden: Wenn ich jemandem etwas als Allheilmittel verkaufe, wird er nicht begeistert sein, wenn es sich einfach nur als gute Medizin für viele, aber eben nicht alle erdenklichen Krankheiten entpuppt. In solchen Fällen werden Menschen verständlicherweise enttäuscht sein. Und als Profession ist das nicht in unserem Sinne. Denn TDD ist eine sehr wirkungsvolle und nützliche qualitätssichernde Methode, die in den meisten Projekten und durch die meisten Entwickler keine Anwendung findet. Und damit gehen jeden Tag viele Chancen auf eine bessere Softwarequalität verloren.
Ein blindes Huhn findet auch mal ein Korn. Ich habe diesen Artikel gerade eben entdeckt und gelesen.
Er ist hervorragend!
In meinem eigenen Blog konnte ich die selben Gedanken im Beitrag https://software-sunshine-blog.de/nachhaltige-softwareentwicklung leider nicht so gut ausdrücken.