So unterschiedlich können Flow-Designs sein. Ich hatte es schon geahnt, doch nun ist es öffentlich.
Am 1.11.2013 hatte ich ermuntert, Flow-Designs für die Kata Ordered Jobs “einzureichen”. Auslöser dahinter war ein Flow-Design, dass Entwickler eines Kunden von mir in einem Coding Dojo unter sich erarbeitet hatten. Sie wollten einfach Flow-Design üben. Das sah so aus, als sie mich schließlich um Rat fragten:
Was mich daran überraschte: Dem Lösungsansatz ist nicht anzusehen, um welches Problem es eigentlich geht. “Process”, “Release”, “Save”, “Output” sind ganz allgemeine Begriffe. Auch der fließende “Job” erhellt nicht wirklich, wie (!) die Lösung funktioniert. Die Musik spielt in “Process” – aber genau die Funktionseinheit wurde nicht verfeinert.
Ob das auch anderen Interessenten an Flow-Design so geht/gehen würde? Das wäre schade, denn ich glaube, so führt Flow-Design zu keiner großen Verbesserung der Verständlichkeit und Evolvierbarkeit von Software.
Es ist ja gerade der Trick an Flows, dass bei ihnen die Syntax so minimal ist, dass sie eine Domänensemantik nicht verrauscht.
Fünf “Einsendungen” hat es dann auf meinen Aufruf hin gegeben. Vielen Dank an die fleißigen Entwerfer! Die möchte ich hier allerdings nicht in ihrer Funktionstauglichkeit bewerten, sondern nur formal gegenüberstellen. Wie sich zeigt, gibt es nämlich mehrere Aspekte, in denen sie sich unterscheiden.
Darstellung
Flow-Design setzt auf… Flüsse. Der Name ist Programm. Die können handgemalt sein wie oben oder in einem Tool wie Visio ordentlich “gesetzt” werden:
Überrascht hat mich daher die folgende Darstellung:
Aber nicht wegen der “squiggly lines” bin ich überrascht, sondern wegen der Gewichtung der Darstellung. Hier sind zwar auch Pfeile im Spiel und stehen für Datenflüsse. Doch prominenter scheinen mit die Abhängigkeiten. Da ist ein DGraph von mehreren Flow-Funktionseinheiten abhängig. Oder? Nein, umgekehrt. Jetzt sehe ich es: Die Funktionseinheiten sind vom DGraph abhängig. Ich war nur einen Moment verwirrt, weil die Abhängigkeiten von unten nach oben zeigen. Das ist eher ungewöhnlich (in Flow-Designs allemal), finde ich.
Im oberen Entwurf gibt es auch eine Abhängigkeit (zu “nextOrderNumber”). Doch die ist zurückhaltender dargestellt; sie steht dem Fluss nicht im Weg.
Die Darstellung bzw. Gewichtung von Abhängigkeiten unterscheidet sich also in Flow-Designs durchaus. Das ist bis zu einem gewissen Grad auch ok, finde ich. Doch wenn die Abhängigkeiten scheinbar die Oberhand gewinnen, wenn der Fluss nicht mehr im Vordergrund steht, dann finde ich einen Vorteil von Flow-Design verschenkt.
Detaillierungsgrad
Wie das erste Diagramm zeigt, können Flow-Designs sehr unspezifisch sein. “Process” passt auf nahezu jedes Problem. Ähnlich empfinde ich aber auch noch bei “Sort” im dritten Diagramm – zumindest solange dann nicht weiter verfeinert wird. So bleibt die Lösung im Dunkeln; die Last liegt voll auf der Codierung und ist damit die Verantwortlichkeit nur eines Entwicklers.
Dito im folgenden Entwurf, wo noch klarer wird, dass die Aufgabe eigentlich nicht vom Flow, sondern von der Datenstruktur DGraph gelöst wird. So eine Datenstruktur samt Algorithmus zur topologischen Sortierung “einzukaufen”, ist natürlich legitim. In der Praxis würde man es womöglich sogar anstreben, um sich eigenen Aufwand zu sparen. Doch das funktioniert natürlich nur, wenn es eine Lösung “einzukaufen” gibt. Und hier war es an der Aufgabe vorbei, da es ja gerade darum ging, selbst einen Lösungsansatz zu formulieren.
Andererseits können Flows natürluch auch sehr detailliert sein:
In diesem und weiteren Flows der “Einreichung” wird der Algorithmus genau beschrieben. Jede Funktionseinheit wird mit 2-3 Zeilen Code umsetzbar sein, schätze ich. Für mich ist das sogar zu detailliert, zumindest für die Implementierung. Bei Entwurf mag man mal so tief einsinken… doch dann würde ich wieder einige Ebenen nach oben steigen beim Codieren. Nicht für jede Funktionseinheit lohnt wirklich eine eigene Funktion.
Lösungen können also zu grob sein, dann helfen sie nicht wirklich. Andererseits können sie auch zu detailliert sein, dann fangen sie an zu rauschen. Die Kunst besteht mithin darin, eine angemessene mittlere Granularität zu finden. Die ist natürlich ein Stück subjektiv und erfahrungsabhängig ;-)
Abstraktion
Flows entwerfen, ist etwas anderes als zu codieren. Es geht um Abstraktion von den Niederungen der textuellen Programmiersprachen. Nur dadurch gewinnt der Entwurf Geschwindigkeit und Überblickscharakter. Es geht darum, eine Karte zu schaffen, nicht eine (Miniatur)Landschaft zu herzustellen.
Auch hier gilt es, die Balance zu finden. Am einen Ende des Spektrums ist der Globus, d.h. eine sehr grobe Karte, die alles zeigt. Das ist völlig ok, nein, unumgänglich – sollte nur nicht wie im vorletzten Diagramm dabei bleiben. Die schrittweisen Verfeinerungen, die auch im Code erhalten werden können und sollen, machen das Flow-Design aus.
In die falsche Richtung geht es aus meiner Sicht jedoch, wenn sich in die Karten Artefakte aus dem Terrain einschleichen. Das ist der Fall, wenn der Fluss nicht mehr deklarativ ist, sondern imperativ wird. Das ist hier z.B. der Fall:
Ein “ForEach” ist beim Flow-Design fehl am Platze; der Begriff steht erstens für eine explizite Kontrollanweisung, wo es doch um Datenflüsse geht, und zweitens ist er frei von jedem Domänenbezug.
Dicht darauf folgen Schleifen jeder Art, z.B.
Sie lassen sich einfach nicht mit dem Integration Operation Segregation Principle (IOSP) in der Implementation mit 3GL vereinbaren.
Ein Flow-Designs beschreiben Datenflüsse, dazu gehören mindestens zwei Punkte, um eine Strecke für den Fluss abzustecken und ihm eine Richtung zu geben. Andererseits dürfen die Flüsse aber auch nicht umkippen und zu Kontrollflüssen degenerieren. Abstraktion im Entwurf bedeutet also, soweit wie möglich eine deklarative Domänensprache zur Lösung eines Problems zu finden.
Anforderungstreue
Überrascht hat mich schließlich auch die Kreativität der Lösungen. Die Vorgaben waren ja klar. Es galt, ein Interface zu implementieren.
Doch das hat nicht in allen Lösungen dazu geführt, auch dieses Interface sichtbar zu machen. Manchmal wurde aus dem geforderten Register() nur ein AddJob(). Manchmal war solch eine Funktionalität aber auch gar nicht zu sehen:
Am anderen Ende des Spektrums dann wieder eine recht treue Abbildung der Anforderung:
Kreativität ist bei der Findung eines Lösungsansatzes selbstverständlich wichtig. Allerdings sollte sie sich dort austoben, wo die Anforderungen notwendig Lücken haben. Das, was der Kunde beschreibt, das, was klar erkennbar ist, sollte hingegen treu übernommen werden. Sonst leidet die Verständlichkeit. Man bedenke immer den anderen Entwickler oder sich selbst in der Zukunft. Da wird dann U in den Anforderungen gelesen, aber im Entwurf ist ein X zu sehen…
Ich weiß, das ist manchmal schwer. Da hat man seine Coding-Standards. Da wallt die Erfahrung in einem auf. Das will alles berücksichtigt werden. Doch ich glaube, wir tun gut daran, uns solchen Regungen nicht gleich hinzugeben. Etwas Disziplin in der Beschränkung der Kreativität hilft der Verständlichkeit.
Fazit
Dass die Entwürfe so weit auseinander gehen, hätte ich doch nicht gedacht. Auch mit den überschaubaren Mitteln des Flow-Design ist also große Bandbreite möglich. Einerseits schön – andererseits aber auch wieder eine ernüchternd. Die guten alten Tugenden Maßhaltung und Genauigkeit und Fokussierung gelten auch im Flow-Design.
Mir hat der Vergleich der Entwürfe sehr geholfen. Und ich hoffe, dass es Ihnen ein bisschen auch so geht.