02 - Vorläufig codieren im Chaos

Mit der Codierung beginnst du erst, wenn du Klarheit über einen Lösungsansatz hast. Das ist eine der wichtigsten Folgerungen aus den Phasen zur Überwindung der Anforderung-Logik Lücke. Codierung ist also nicht gleichzusetzen mit Programmierung, sondern nur ein Teil davon.

Der Lösungsansatz wird in Form eines Modells vom Entwurf geliefert, das in Beziehung gesetzte Funktionen enthält. Dazu kommen Testfälle für diese Funktionen, zu denen ja auch als Ausgangspunkt die API-Funktionen der Analyse gehören.

Konkret bedeutet das in den meisten Fällen: Die Codierung bekommt Klassen (allgemeiner: Module) und Funktionen vorgelegt, die sie in eine Programmiersprache übersetzen und dann mit Logik füllen soll.1

Das Nein der Codierung

Eigentlich sollte der Startpunkt deiner Codierungsphase jedenfalls so aussehen. Eigentlich solltest du konkret mit einer Funktion(signatur) und zugehörigen Testfällen in der Hand vor dem blinkenden Cursor deiner IDE sitzen. Nicht weniger Informationen solltest du haben.

Es kann jedoch sein, dass dir die Analyse der Anforderungen kein formalisiertes Verständnis geliefert hat, was dein Auftraggeber will. Vielleicht fehlen konkrete Testfälle, vielleicht sind zwar Nutzungsbeispiele grob dokumentiert und besprochen, aber es fehlt noch eine Vorstellung über deren Repräsentation im Code durch API-Funktionen. Die Unklarheit kann viele Formen und Ursachen haben. Wichtig ist, dass du dir ihrer bewusst bist!

Vor der Codierung kannst du also auf zwei sehr unterschiedlichen Stufen stehen:

  • Mit (Akzeptanz-)Testfällen und zugehörigen Funktionen
  • Ohne Testfälle und/oder zugehörige Funktionen

Diese Unterscheidung ist wichtig, denn Produktionscode, d.h. der auszuliefernde Code, sollte nur geschrieben oder angefasst werden in größtmöglicher Klarheit. Es besteht sonst die Gefahr unnötiger Veränderungen, die eine Belastung für die Produktionscodestruktur darstellen. Wenn du in Unklarheit den Produktionscode erst in die eine Richtung zerrst und negatives Feedback bekommst, ihn dann in eine andere richtig drückst und wieder negatives Feedback bekommt und dann nochmal durch die Mangel drehst… dann verbessert sich seine Struktur dabei nicht. Im Frust wirst du Abkürzungen nehmen und wenig geneigt sein, aufzuräumen.

Sobald du einen Lösungsansatz in Code “gießt”, verhärtet er. Code, auch wenn er nur aus programmiersprachlichem Text besteht, ist schwer zu verändern. Wenn du ihn also anfasst, solltest du dir sehr sicher sein, dass und wie das nötig ist.

Ein guter Teil der Unordnung in Code, die die Produktivität so negativ beeinträchtigt, hat als Ursache “vorzeitige Codierung”. Die kann als eine Art von premature optimization angesehen werden, also einer Variante der Wurzel allen Übels nach Donald Knuth.

Wer mit der Herstellung/Veränderung von Produktionscode beginnt, ohne ausreichend sicher zu sein, dass Struktur und Logik so und nicht anders sein sollten, der zielt auf schnelle Lieferung ab. Das ist eine Optimierung auf Feedback oder Wert hin – aber auch das kann selbst in einem agilen Vorgehen vorzeitig geschehen. Um dem entgegen zu wirken, lautet die klare wie pauschale Empfehlung:

Ein solcher Test setzt voraus, dass Beispieldaten vorliegen und mindestens eine Funktion existiert, die den Funktionsbaum repräsentiert, in dem Logik verändert werden muss.

Sind diese Voraussetzungen für ein Issue nicht erfüllt, darfst keinen Produktionscode schreiben. Es ist schlicht nicht wirklich klar, wie das gewünschte Verhalten aussehen soll.

Deshalb musst du spätestens jetzt Nein zu einer Veränderung von Produktionscode sagen!

Ich weiß, das ist sehr schwer. Der Druck vom Auftraggeber, nach all den Gesprächen über Anforderungen endlich zu beginnen ist groß. Sollte ein iterativ-inkrementelles Vorgehen nicht die Risiken von Unklarheiten auch schon genug minimieren? Ich glaube, nein. Es gibt Unklarheit, die so groß ist, dass du sie nicht in einer “normalen” Iteration aus der Welt schaffen kannst, bei der du etwas mit Produktionscode ausprobierst. Und das Kriterium ist für mich die Existenz von codierbaren Akzeptanzkriterien. Da, so glaube ich, solltest du eine Linie ziehen und sagen “Bis hierher und nicht weiter!” Entweder, du hast mit dem Auftraggeber genügend Klarheit hergestellt in Form von Akzeptanztestfällen und API-Funktion - oder eben nicht. Wenn es dem Auftraggeber so wichtig ist, dass du produktiv wirst, d.h. Produktionscode erweiterst, dann muss er sich eben anstrengen, für sich Klarheit herzustellen, was er denn von dir will. Falls das klappt, beginnst du gern die Arbeit am Produktionscode. Falls aber nicht…

Prototyping to the Rescue

Falls keine konkreten Akzeptanzkriterien vorliegen, bedeutet das natürlich nicht, dass du dich trotzig auf die Hinterbeine stellst und nichts tust. Mit Codierung kannst du dem Auftraggeber auch in dieser Situation der Unklarheit zu Diensten sein - nur eben nicht mit Produktionscode.

Du kannst auf andere Weise versuchen, dem Auftraggeber etwas zu geben, was ihn zu Feedback anregt. Jetzt schlägt die Stunde des Prototypen! Prototypen sind Werkzeuge zur Informationsgenerierung. Mit ihnen kitzelst du Klarheit aus dem Auftraggeber (und auch aus dir) heraus.

Prototypencode kann jede erdenkliche Form haben, ist aber strickt getrennt vom Produktionscode. Selbstverständlich kann prototypischer Code jedoch Elemente des Produktionscodes benutzen. Es zahlt sich also aus, den Produktionscode so zu strukturieren, dass er in Bausteinen vorliegt, die in Prototypen “wiederverwendbar” sind. Die Notwendigkeit von Prototypen angesichts immer wieder herrschender Unklarheit, sollte mithin eine Motivationsquelle für die Modularisierung von Produktionscode sein.

Modularisierung sei an dieser Stelle pauschal und vereinfachend verstanden als eine Zusammenfassung von Logik in Funktionen in Klassen. Eine genauere Definition des Begriffs Modul gehört zu den Erläuterungen der Entwurfsphase.

Die Natur von Prototypen im Allgemeinen und prototypischem Code im Besonderen ist die Vorläufigkeit! Prototypen haben keine lange Lebensdauer, so dass bei ihrer Herstellung Korrektheit und Ordnung keine spezielle Rolle spielen. Beim Prototypen geht es allein darum herauszufinden, was genau Wert für den Auftraggeber bedeutet.

Hier ein Beispiel für einen Prototypen für Iteration 2 des Begrüßungsprogramms aus Lektion 1:

Der Prototyp ist nur eine Skizze der Bedienung des Programms. Er besteht aus etwas Text, nicht einmal Code ist nötig. Aber das reicht aus, um dem Auftraggeber Feedback zu entlocken: “Haben Sie sich so die Bedienung des Programms vorgestellt? Meinten Sie, dass der Name auf der Kommandozeile mitgeteilt werden soll?”

Und es zeigt eine Kerneigenschaft von Prototypen: sie sind leichtgewichtig. Wo Unklarheit herrscht, soll ohne Ballast Klarheit geschaffen werden. In Unklarheit sind Ordnung und Korrektheit noch nicht wichtig.

Unklarheit in Bezug auf die Bedienung einer Software ist ein häufiger Grund für schlechte Anforderungen. Die wachsende Zahl an UI-Prototyping Werkzeugen bezeugt das. Doch diese Unklarheit in Bezug auf die Form sollte nicht verwechselt werden mit einer Unklarheit in Bezug auf den Inhalt, d.h. die Funktionalität. Nur, weil der Auftraggeber nicht recht weiß, wie die Bedienung genau aussehen/sich anfühlen soll, können keine Beispiele für die grundlegend geforderten Transformationen formuliert werden? Hier ist die Softwareentwicklung gefordert, differenziert hinzuschauen und auch den Auftraggeber herauszufordern.

In Bezug auf Iteration 2 könnte das bedeuten, dass du mit dem Auftraggeber auch bei Unklarheit in Bezug auf die Benutzerschnittstelle schon Klarheit in Bezug auf das grundlegende Verhalten erzielen kannst. Dafür ist nicht die Darstellung wichtig, sondern die Datenverarbeitung, die Übersetzung von Input in Output. Das könnte z.B. so aussehen:

Denn daraus kannst du zumindest schon eine API-Funktion ableiten, z.B. string Greet(string name). Mit einem solchen Vorgehen wäre die Unklarheit zurückgedrängt. Nicht alles ist unklar, sondern “nur” die Bedienung der Software. Du könntest also mit Produktionscode für die Übersetzung schon beginnen, während du bei der Benutzerschnittstelle noch im Prototypenmodus arbeitest.

In jedem Fall sind Prototypen kurzlebige Werkzeuge zur Generierung von Feedback in Bezug auf sehr spezifische Aspekte von Software, egal, ob es sich um die Benutzerschnittstelle oder Funktionalität handelt. Es sind Sonden, die “an den Auftraggeber angelegt werden”, um seine Reaktion zu messen. Es ist hinlänglich bekannt, dass Auftraggeber nur schwer sagen können, was sie wollen - doch sie können sehr gut erkennen, was sie nicht wollen, wenn man ihnen Alternativen vorlegt. Du kennst sicher die Reaktion von Auftraggebern “Nein, doch nicht so! Das geht gar nicht!”, wenn du ihnen etwas präsentierst, was du nach ihren unklaren Vorstellungen mit viel Mühe erarbeitet hast. Auftraggeber sind sehr, sehr gut darin, Differenzen zu ihren Vorstellungen zu erkennen - auch wenn sie ihre Vorstellungen nicht artikulieren können.

Ein Text-Prototyp für die Benutzerschnittstelle mit zwei Alternativen

In Situationen der Unklarheit hilft es daher, zügig dem existierenden Zustand S einen Zustand T nebenzuordnen und zu fragen, “Ist das besser? War es so gemeint?” Damit wird einerseits klar gemacht, was die Softwareentwicklung bisher meint verstanden zu haben (dass T vom Auftraggeber gewünscht sein könnte); andererseits kann der Auftraggeber etwas sehen/fühlen, statt sich nur etwas vorzustellen, was es ihm leicht macht zu erkennen, ob es noch einen Kontrast zu seiner bisher schwammigen Qualitätserwartung gibt.2

Prototypen bauen einen Sandkasten auf, in dem du Softwareentwicklung spielerisch “austoben” kannst. Du kannst Feedback vom Auftraggeber generieren. Du kannst aber auch nur für dich persönlich Spikes herstellen, um zu eruieren, ob/wie Technologien genutzt oder Lösungsansätze codiert werden könnten.

Unklarheit kann also außen wie innen herrschen. In beiden Fällen solltest du dringend mit Prototypen forschen, bevor du Produktionscode anfasst.

Dass dann prototypischer Code wesentliche Qualitäten von Produktionscode vermissen lässt, liegt in seinem Zweck. Es verbietet sich daher, prototypischen Code “einfach so” in Produktionscode umzumünzen oder ohne weitere Nacharbeit dorthin zu kopieren.

Code aus einem Prototypen muss auf dem Weg in den Produktionscode in puncto Korrektheit und Ordnung auf das notwendige Niveau für langfristig hohe Produktivität gehoben werden. Angemessene Tests sind einzurichten, angemessene Strukturen sind zu entwerfen und zu implementieren.

Nach einem Prototypen soll klar sein, wie Testfälle für eine Anforderung auszusehen haben und welche Funktionen ihnen gegenüberstehen sollen.

Prototyping ist insbesondere eine Unterstützung der Analyse durch Codierung. Die Softwareentwicklung ist nicht untätigt - doch sie muss klar machen, dass ihre Arbeit keinen Produktionscode erzeugt, sondern “nur” ein vorläufiges Analysehilfsmittel.

Mit diesem Nein gegenüber dem Auftraggeber schützt du deine Kapazität für die Herstellung von Features im Produktionscode. Würdest du das nicht tun und vorzeitig mit der Veränderung von Produktionscode beginnen, wären später verschwenderische Nacharbeiten in Form von Bug Fixing und Refaktorisierungen die Folge.

Die Softwareentwicklung hat die Verantwortung, die von ihr betreute Ressource Produktionscode zu schützen. Dazu gehört ein klares Nein in Unklarheit, was Verständnis von Anforderungen oder auch Technologien angeht.

Die Schwierigkeit der Umsetzung einstufen

Die an der Codierung anliegenden Anforderungen bzw. ihre Lösungsansätze sind unterschiedlich schwierig umzusetzen mit Code im Allgemeinen und Logik im Speziellen. Mehr oder weniger schwierig bedeutet, dass die Herstellung des Ergebnisses mehr oder weniger lange braucht und/oder mehr oder weniger punktgenau die gewünschte Qualität haben wird.

Anforderungen, die nicht schwierig sind, können schnell und verlässlich mit allen Qualitäten codiert werden. Anforderungen, die hingegen sehr schwierig sind, brauchen mehr, gar viel mehr Zeit oder sogar unbekannt lange, bis sie womöglich nur mit unvollständiger Qualität geliefert werden.

Wie schwierig ist aber eine bestimmte Anforderung (z.B. formuliert als User Story) umzusetzen? Die Agilität lässt Entwickelnde das oft mittels einer Story Point Schätzung grob beurteilen. Damit bekommt der Scrum Product Owner als Auftraggeber einen Eindruck davon, wie viel Mühe eine Umsetzung machen könnte. Diese Angabe kann er dann in Bezug setzen zum Wert, den er einer User Story beimisst, um ihr eine Priorität in der Entwicklungsreihenfolge zu geben oder über eine weitere Verfeinerung (Granularität) zu entscheiden.

In der Agilität dient die Einstufung des Umsetzungsschwierigkeitsgrades von Anforderungen der Steuerung der Entwicklung. In der Codierung hingegen, wenn schon entschieden ist, dass eine Anforderung umzusetzen ist, dient sie der Wahl der Methode, wie die Umsetzung betrieben werden soll.

Das Kriterium “Verständnis liegt dokumentiert durch Testfälle und Funktionen vor” dient einer ersten groben Einschätzung des Schwierigkeitsgrades. Wird das Kriterium nicht erfüllt, ist der Schwierigkeitsgrad im Grunde unbekannt hoch.

Problemschwierigkeit ist für mich ein Kontinuum. Das reicht von trivial bis unbekannt. Dieses Kontinuum wird durch das Kriterium mithin in mindestens zwei Bereiche geteilt: Anforderungen, für die keine codierbaren Akzeptanzkriterien vorliegen, liegen darin pauschal im Chaos.

Die Bezeichnung “chaotisch” ist dem Cynefin Framework entlehnt. Sie soll darauf hinweisen, dass im Angesicht von Chaos die angemessene Reaktion das unverzügliche Tun ist. “Taten statt Warten” ist angezeigt.

Mit unverzüglichem Tun meine ich natürlich nicht Aktionismus. Wildes Flügelschlagen ist kontraproduktiv. Vielmehr soll das, was getan wird, gegründet sein in einem Wertesystem. Das bedeutet, es gibt ein Fundament an Regeln und Prinzipien, die der Ordnung der Welt dienen. Nur wenn eine solche Ordnung herrscht, ist sichergestellt, dass Ziele erreicht werden können.

Für das gesellschaftliche Leben gehört zum Wertesystem z.B. Ehrlichkeit oder Rechtsstaatlichkeit. Wo Menschen im Miteinander nicht mehr erwarten können, dass ihr Gegenüber ebenfalls diese Werte schätzt und beherzigt, herrscht Chaos. Und umgekehrt, wo gesellschaftliches Chaos herrscht, besteht die unverzügliche Tat darin, ehrlich und rechtsstaatlich zu handeln.

In der Codierung, wie hier vorgestellt, gilt als Wert “Produktionscode steht hinter einer Funktion unter automatisiertem Test”. Ist das nicht der Fall, herrscht eben Chaos. In der Literatur wird diese Form von Code auch Legacy Code genannt:

“Michael Feathers introduced a definition of legacy code as code without tests, which reflects the perspective of legacy code being difficult to work with in part due to a lack of automated regression tests.”

Produktionscode ohne automatisierte Tests “als Gegengewicht” bzw. “Sicherheitsnetz” ist chaotisch nicht aufgrund seiner Struktur, sondern aufgrund der fundamentalen Unsicherheit, was passiert, wenn man ihn verändert. Ob man den Effekt erzielt, den man erzielen will, kann nicht zügig, nachvollziehbar und personenunabhängig festgestellt werden. Es besteht keine klare Grenze zwischen korrekt und inkorrekt.

Wenn automatisierte Tests ausgeführt werden, ist unzweideutig klar, ob Produktionscode diesseits oder jenseits dieser Grenze steht.

Zusammenfassung

Automatisierte Tests lassen in der Softwareentwicklung mit ihrer Grenze eine fundamentale Dualität entstehen: Korrektheit vs. Inkorrektheit. Sie ist genauso fundamental wie die von Tag und Nacht, Himmel und Erde, Gut und Böse.

Die Einführung von Dualitäten lassen in Schöpfungsgeschichten Ordnung aus Chaos entstehen. Dasselbe leisten automatisierte Tests. Ihr Vorhandensein grenzt chaotische von nicht-chaotischen Umsetzungssituationen ab. Das ist eine erste wesentliche Unterscheidung und damit eine Entscheidung für eine Methode: im Chaos ist Prototyping die Methode der Wahl.

Wenn du Korrektheit und Ordnung herstellen willst, musst du zuerst erkennen, ob dafür überhaupt schon die Grundbedingungen gegeben sind. Im Chaos ist das nicht der Fall. Deshalb diese deutliche Grenzziehung, dieses kategorische Nein zur Arbeit am Produktionscode, solange Testfälle mit zugehörigen Funktionen fehlen.

Das ist der erste methodische Pfeil in deinem Köcher für die Codierung: Fange gar nicht erst mit ihr an, wenn die Anforderungen nicht ein Mindestmaß an Qualität haben. Dieses Mindestmaß besteht im Vorhandensein von automatisierbaren Akzeptanzkriterien. Punkt. Das musst du deinem Auftraggeber immer wieder klar machen. Weniger geht gar nicht - zumindest was Produktionscode betrifft. Du bist ja willig, mit ihm Unklarheit auszuräumen; um dabei Produktionscode zu verändern, hast du aber einen gewissen professionellen Anspruch an dein Verständnis und an die Überprüfbarkeit der Korrektheit deiner Eingriffe.

Ein Akzeptanztest steht mithin für Arbeit am Produktionscode immer am Anfang. So viel test-first muss sein: Erst der Akzeptanztestcode, dann der Produktionscode. Und wenn das nicht geht, dann eben ein Prototyp der einen oder anderen Art.