TDD 2.0 – Problemanalyse, Lösungsfindung, Implementierung

[Update (10/12/2013): Rechtschreibung]

Schon mal bei einem Coding Dojo etwas frustriert gewesen, da absolut keine Lösung in Sicht war? Praktiziert eigentlich jeder Softwareentwickler TDD auf dieselbe Art und Weise? Kann bzw. muss eine so fundamentale Vorgehensweise wie TDD weiterentwickelt werden? Ralf Westphal meint ja!

Wer mich kennt, der weiß, dass ich ein Fanboy von Ralf Westphal bin. Warum? Ich finde, dass es nicht besonders viele Personen in meiner Branche gibt, die bestehende Dinge hinterfragen und vorantreiben. Ralf ist allerdings für mich genau einer dieser Menschen, die grundlegende Bereiche der Softwareentwicklung verbessern möchten. Ein Beispiel dafür ist die Testgetriebene Entwicklung, die vor ca. 10 Jahren von Kent Beck ins Leben gerufen wurde. Täglich stelle ich fest, dass diese Disziplin noch nicht wirklich in Wien (bzw. Österreich) bei der Entwickler-Community angekommen ist.

Auch bei aktuellen Konferenzen werden nach wie vor jede Menge Einführungsveranstaltungen zum Thema TDD angeboten. Von Universitäten und Fachhochschulen will ich gar nicht reden. OK, an eine Vorlesung erinnere ich mich, in der das Thema kurz einmal erwähnt, ja sogar demonstriert wurde. Unterm Strich obliegt es jedoch jedem selbst diese so wichtige Disziplin zu erlernen und ständig zu üben.

Ralf Westphal hat aus meiner Sicht eine sehr beeindruckende Artikelserie in der dotnetpro geschrieben, die meiner Meinung nach öffentlich kostenlos zugänglich gemacht werden sollte, um möglichst viele Entwickler damit zu erreichen. Im ersten Teil dieser Serie kritisiert er die gegenwärtige Praxis von TDD. Zum Teil entsprechen diese Kritiken meinen persönlichen Erfahrungen. Die beiden Hauptpunkte sind: mangelndes Refactoring, d.h. die dritte Phase von TDD wird nur marginal von Entwicklern durchgeführt, und die nicht vorhandene Trennung von Lösungsfindung und Implementierung.

Kritik: Mangelndes Refactoring

Warum funktionieren die ersten beiden Phasen, Red + Green, besser als die letzte? Diese Frage stellt sich auch Ralf. Ist es mangelndes Wissen bezüglich prinzipiengetreuen Programmierens? Fehlender Spaß an der Sache? Geht es einfach schlicht weg auch ohne? Es ist mit Sicherheit eine Kombination aller dieser Dinge.

Ich habe TDD so gelernt, dass man in der Red-Phase immer nur genau so viel Produktionscode schreibt, bis Tests grün sind. Dadurch vermeidet man Code zu schreiben, den man nur eventuell brauchen könnte. So entstehen Lösungen die dem KISS Prinzip entsprechen. Das Problem damit ist, dass die Verständlichkeit dadurch nicht gerade  erhöht wird. Natürlich entfernt man Duplikation und achtet darauf Magic-Numbers/Strings zu vermeiden; und klar ist es leichter, in KISS basierten Implementierungen Erweiterungen durchzuführen als in 1000-zeiligen Methoden. Jedoch muss man nach wie vor Codearchäologie betreiben um den Code verstehen zu können. KISS is not enough!

Mir persönlich stellt sich auch immer wieder die Frage: Wann habe ich genug Refactoring betrieben? Oder anders formuliert: Wann habe ich genug Struktur in meiner Implementierung untergebracht um Evolvierbarkeit zu ermöglichen und Nachvoll-ziehbarkeit für den nächsten Bearbeiter zu gewährleisten? Deshalb programmieren wir in der Arbeit ausschließlich zu zweit (und das nicht nur zur Fehlerbehebung bzw. wenn es Probleme gibt).

Der Punkt ist, es gibt keine klaren Regeln zur dritten Phase von TDD. Noch dazu ist Design aka Refactoring zum großen Teil subjektiv. Ralf ist der Meinung, dass fehlender Druck zum Refactoring ein Grund für die aktuell gelebte TDD-Praxis sein könnte. Dabei bezieht sich der fehlende Druck auf die Methode selbst.

Kritik: Fehlende Phase zur Lösungsfindung

Wenn man TDD betreibt dann muss einem eines ganz klar bewusst sein: TDD führt nicht automatisch zur Lösung eines Problems. Ohne Denken geht es einfach nicht! Schon öfters habe ich bei Coderetreats feststellen müssen, dass Entwickler beim Programmieren ins Stocken geraten. “Welchen Test schreiben wir denn jetzt? Uff, da müssen wir aber jede Menge umbauen!” Das Problem ist, dass wir gleichzeitig zu lösen und zu implementieren versuchen.

Ralf sagt dazu folgendes [1]:

Zwischen Testcode und Produktionscode liegt immer Nachdenken, das heißt die Entwicklung einer Lösung im Kopf des Programmierers. Wenn das jedoch während des Codierens stattfinden soll, ist der Prozess eingeschränkt. Dazu müsste der Programmierer präemptives Multitasking betreiben und immer wieder zwischen Codieren und Lösen wechseln. Das sind zwei kreative, um Hirnkapazität konkurrierende Tätigkeiten.

TDD 2.0

Wie sich vielleicht erahnen lässt, gehört die Trennung zwischen Lösungsfindung und der Implementierung (Red+Green+Refactoring) unter anderem zum Verbesserungsvorschlag von Ralf. Ebenfalls wird die Problemanalyse explizit der Lösungsfindung vorangestellt. Man erhält folgende drei Phasen für TDD 2.0:

image

Die Abbildung habe ich von Ralfs Blog geborgt. Man findet sie hier. Diese wird auch in der dotnetpro Artikelserie mehrmals verwendet.  Für Ralf handelt es sich dabei um den minimalen Softwareentwicklungsprozess je Inkrement. Weniger geht nicht!

Wenn man heutzutage TDD gelehrt bekommt, sei es durch erfahrene Entwickler, durch Bücher oder eventuell durch Videos im Web, wird ausschließlich die Refactoring-Phase zum Designen verwendet. Kent Beck schreibt in seinem Buch Test-Driven Development By Example:

1. Write a test. …
2. Make it run. …
3. Make it right. …

oder

First we’ll solve the ‘that works’ part of the problem. Then we’ll solve the ‘clean code’ part.

Dadurch entsteht Codestruktur relativ spät und überraschend. Auch ich habe es bis jetzt hauptsächlich so geübt, jedoch halte ich die Refactoring-Phase strikt ein. Schon alleine aus dem Grund weil sie mir einfach Spaß macht. Trotzdem entstehen dadurch Abstraktionen erst, wenn ich z.B. Duplikation entferne. Zu spät aus Ralfs Sicht. Warum hoffen, dass Struktur nur nach mehrmaligen Refactoring entsteht? Außerdem darf es niemals ausgelassen/vergessen/verschoben werden. Leider zeigt die Praxis jedoch Gegenteiliges.

Ralf meint:

Schluss mit Überraschungen!

In TDD 2.0 macht man sich schon in der Lösungsphase Gedanken zum Design. Durch tiefes Problemverständnis versucht man eventuelle Aspekte herauszuarbeiten, die in der Lösung vorkommen sollen. Wenn man bis jetzt immer einfach darauf los gecoded hat, dann kann es zu Beginn schon ein bisschen schwer fallen, die IDE zuerst vorab geschlossen zu lassen. Zunächst nur Zettel und Stift nehmen, die Lösung versuchen zu visualisieren und aussagekräftige Bezeichnungen zu finden. Verrückt oder?

Aber halt! Schalten wir einen Gang zurück. Man wäre gut beraten, die erste Phase in TDD 2.0 nicht auszulassen. Die ist nämlich absolute Voraussetzung zur Lösungsfindung. In dieser geht es darum, ein tiefes Verständnis für die Problemdomäne zu entwickeln. Hat man das Problem nicht verstanden, dann ist es natürlich auch nicht möglich eine Lösung zu finden. Klingt logisch oder? Ich kann mich noch genau an mein erstes Coderetreat erinnern. Bereits in der ersten Session wurde von meinem Pairing Partner folgendes gesagt: “Na schreib einfach einmal einen Test”. Ich habe mich dem Druck gebeugt und einfach mal drauf los getippselt. Nicht besonders hilfreich. Mehr darüber werde ich einmal in einem separaten Post zum Thema Coding Dojos und Coderetreats berichten.

Ich habe oft den Eindruck, dass es in der Natur des Entwicklers liegt, lieber in eine IDE zu starren als vorab das Problem zu diskutieren. Schon oft habe ich gemerkt, dass mein Pairing Partner lieber einmal drauflos tippen würde, anstatt mit mir das Problem näher zu analysieren. Dadurch würde man sich viel Zeit sparen, da man nicht ins Stocken gerät bzw. mitten in der Implementierung auf Dinge stößt, die in Folge einen beträchtlichen Umbau erfordern.

Bewusstes Zeit nehmen für alle drei Phasen, um das geht es in Wirklichkeit in TDD 2.0. Bevor man sich das nächste Mal an die Implementierung eines neuen Features heranwagt, könnte man doch mal folgende Checkliste beachten:

IMG_0359

Auf den dritten Punkt der Checkliste bin ich bisher noch überhaupt nicht eingegangen. Woher weiß man eigentlich, welcher Testfall der nächste ist. Meiner Erfahrung nach, wird diese Entscheidung von Entwicklern relativ ad hoc getroffen. Ich habe es eigentlich noch nie erlebt, dass vorab Testfälle von jemanden überlegt und nach Schwierigkeitsgrad priorisiert wurden. Ohne dem kann es während der Implementierung zum Stillstand kommen, da man nicht so recht weiß, welcher Test dann der nächste ist. Eindeutig ein Zeichen dafür, dass man zu wenig priorisiert hat. TDD 2.0 bietet jedoch hier die Möglichkeit in die früheren Phasen zurückzuspringen. Wenn man allerdings schon Probleme bei der Priorisierung hat, dann muss man höchstwahrscheinlich noch weiter an der Lösungsfindung bzw. an der Problemanalyse arbeiten.

Wie geht’s weiter?

Der aufmerksame Leser wird bemerkt haben, dass diese Zusammenfassung ausschließlich auf die Verbesserung einer der beiden Kritikpunkte eingeht, nämlich auf den der Trennung von Lösungsfindung und Implementierung. Druck zur Refaktorisierung wird weiterhin nicht durch die Methode selbst ausgeübt. Darüber werde ich in meinem nächsten Post berichten.

Vertiefung

Ralf hat glücklicherweise schon einige Artikel über seinen Blog veröffentlicht die ebenfalls diese hier zusammengefasste Thematik behandeln. Wem TDD interessiert und wer gerne ein paar Beispiele zu diesem Material hätte, dem sei dringend empfohlen Ralfs Beiträge zu lesen.

Sehr zu empfehlen sind auch die dazugehörigen Kommentare von Ralf. Also auch diese unbedingt lesen. Da gibts noch mal richtig viel Input und Erklärung zum Material.

Wer die originale dotnetpro Artikelserie kaufen möchte, der findet sie hier:

[1] Teil 1 (03/2013)

Über sageniuz

https://about.me/ClausPolanka
Dieser Beitrag wurde unter Refactoring, Software Craftsmanship, TDD abgelegt und mit , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Eine Antwort zu TDD 2.0 – Problemanalyse, Lösungsfindung, Implementierung

  1. Pingback: TDD 2.0 – Teil 2 | TDD as if you meant it (TDDaiymi) | Sageniuz Personal Space

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s