Follow my new blog

Sonntag, 14. Oktober 2012

Flow Operationen automatisch registrieren

Flow Designs zu implementieren, ist nun einen ganzen Schritt einfacher geworden. Endlich habe ich nämlich Zeit gefunden, die Flow Runtime mit etwas mehr "Intelligenz" zu versehen.

Bisher bestand die Implementation eines Flow Designs aus drei Teilen:

  • Flow
  • Operationen
  • Bindung

Als Beispiel für einen Flow hier der für die Umformatierung einer Datei in ganz einfacher Weise. Ihre Zeilen sollen auf eine neue maximale Länge gebracht werden:

/
.run, Zeilen_lesen
Zeilen_lesen, Umbrechen
Umbrechen, Zeilen_schreiben

Die Operationen sind Methoden, die wohl ganz angemessen auf zwei Klassen verteilt werden können:

class Textdatei {
  private string _dateiname;

  public string[] Zeilen_lesen(string dateiname) {…}

  public void Zeilen_schreiben(string[] zeilen) {…}

class Formatierung {
  public static string[] Umbrechen(string[] zeilen) {…}

Gebunden werden die Operationen an den Flow über die Konfiguration der Flow Runtime:

var td = new Textdatei();

var config = new FlowRuntimeConfiguration()
                   .AddStreamsFrom("umbrechen.root.flow", Assembly.GetExecutingAssembly())

                   .AddFunc<string, string[]>("Zeilen_lesen", td.Zeilen_lesen)
                   .AddAction<string[]>("Zeilen_schreiben", td.Zeilen_schreiben)
                   .AddFunc<string[], string[]>("Umbrechen", Formatierung.Umbrechen);

using(var fr = new FlowRuntime(config)) {
  fr.Process(".run", args[0]);
  fr.WaitForResult();
}

Das funktioniert wunderbar. Doch es ist bei der Konfiguration umständlich. Die stellt nämlich auch einen Widerspruch zu DRY dar. In ihr werden ja Dinge wiederholt, die anderswo schon stehen. Vor allem ist es aber mühsam, in der Konfiguration die Signaturen der Operationsmethoden anzugeben. Das macht die dynamische Natur der kleinen Flow DSL quasi nutzlos.

Damit ist nun Schluss!

Jetzt ist die Konfiguration viel, viel einfacher. Sie schrumpft im Grunde auf zwei Zeilen Code, die sich im Verlauf der Evolution einer Anwendung kaum mehr ändern müssen:

var config = new FlowRuntimeConfiguration()
                   .AddStreamsFrom("umbrechen.root.flow", Assembly.GetExecutingAssembly())
                   .AddOperations(new AssemblyCrawler(Assembly.GetExecutingAssembly()));

Eine Zeile liest den Flow, eine weitere Zeile lädt alle Operationen.

Der AssemblyCrawler durchläuft alle Typen der ihm übergebenen Assemblies und schaut, ob die Operationsmethoden definieren. Wenn ja, registriert er sie automatisch. Berücksichtigt werden Klassen, die mit einem von drei Attributen markiert sind:

[InstanceOperations]
class Textdatei { … }

[StaticOperations]
class Formatierung { … }

Klassen, die mit [StaticOperations] gekennzeichnet sind, werden auf statische Methoden überprüft, Klassen, die mit [InstanceOperations] markiert sind, werden zuerst instanziert, um dann auf Instanzmethoden überprüft zu werden. Und schließlich gibt es noch [EventBasedComponent], um Instanzen zu registrieren, die Output über Events hinausgeben.

Relevant als Operationen sind in jedem Fall nur Methoden, die den Signaturen entsprechen, die auch über AddAction<>() und AddFunc<>() registriert werden können. Einen Unterschied gibt es dabei jedoch: Output-Ports, die über Continuations definiert sind, haben nicht mehr die Namen ".out0" und ".out1", sondern die der Continuation-Parameter.

Der Crawler macht die Registrierung aller Operationen in den übergebenen Assemblies sehr einfach. Wer jedoch etwas mehr Kontrolle haben möchte, kann mit Attributen markierte Klassen auch von Hand registrieren:

var config = new FlowRuntimeConfiguration()
                   .AddStreamsFrom("umbrechen.root.flow", Assembly.GetExecutingAssembly())
                   .AddInstanceOperations(new Textdatei())

                   .AddStaticOperations(typeof(Formatierung)); 

Ich habe jetzt einige Tage mit dieser Form der einfachen Konfiguration gearbeitet… und möchte sie nicht mehr missen. Jetzt endlich kann ich mich wirklich auf Flow und Operationen konzentrieren. Jetzt endlich löst die Flow Runtime ihr Versprechen ein, simpler und gleichzeitig leistungsfähiger als EBC-Verdrahtung zu sein.

Hier geht's zum Download der aktuellen Flow Runtime NPantaRhei.

Sonntag, 7. Oktober 2012

Was andere schon richtig machen - Halden vermeiden


Das ist Wim Wenders. Er ist einer der bekanntesten zeitgenössischen deutschen Regisseure mit Weltruf. Von ihm können wir als Softwareentwickler etwas lernen.

Als Regisseur ist es seine Aufgabe, gewisse Ziele zu erreichen: Er hat ein inhaltliches/künstlerisches Ziel, er hat ein Terminziel und er hat natürlich auch ein Budgetziel. Gerade Termin und Geld zwingen dazu, die Produktion genau zu planen. Das beginnt beim Drehbuch, denn ohne Drehbuch ist ja ungewiss, was an Drehorten, Schauspielern, Bühnbild und schließlich Zeit und Geld überhaupt nötig wäre. Auf dessen Basis entsteht dann ein Drehplan. Der sagt, was wann wie wo genau zu drehen ist, so dass die kosten minimal sind. Was im Drehbuch wie in einer Theaterstück in einer für die Geschichte passenden Reihenfolge steht, wird dafür in Teile zerlegt und neu gemischt. Szenen am selben Ort zu unterschiedlichen Zeiten im Film, werden zusammengelegt, so dass der Drehort nur einmal besucht werden muss. Und was am Ende passiert, wird an den Anfang gelegt. Der Dreh ist mithin nicht chronologisch.

Das bedeutet, etwas das früh gedreht wird, aber erst spät im Film auftaucht, muss 100% so gedreht werden, dass es später passt. Der Regisseur muss also genau wissen und dem ganzen Team klar machen, was vorher passiert sein wird, damit die früh gedrehten späteren Szenen auch brauchbar sind. Sie liegen dann nach der Aufnahme auf Halde. Nicht nur steckt in ihnen jetzt Kapital, sie zwingen den Rest des Drehs auch in eine Form, der sie anschlussfähig macht.

Der nicht-chronologische Dreh ist damit eine lokale Optimierung im Sinne des Geld-/Zeitbudgets - und stellt ansonsten nur eine Belastung dar: Die Continuity steht vor einer großen Herausforderung, um visuelle Sprünge zwischen aneinander geschnittenen Einstellungen zu vermeiden, die mit großem Abstand gedreht wurden. Die Schauspieler stehen vor großen Herausforderungen, da sie Präsenz in ihren Rollen "auf Zuruf" ganz ohne Kontext herstellen müssen. Der Regisseur steht vor der großen Herausforderungen, das Ganze in seinem Zusammenhang und seiner Entfaltung allen Beteiligten immer wieder präsent zu machen, auch wenn jeden Tag nur beliebige Ausschnitte gedreht werden.

Als Resultat entsteht eine wachsende Halde von Takes, deren Qualität im Rahmen eines Ganzen erst im Schnitt deutlich wird. Nicht nur wird sich dann erst erweisen, ob alles so anschlussfähig ist, wie geplant.

Insofern kann denn auch das Drehbuch als Halde angesehen werden. Es ist ein Plan für die Herstellung einer Wirkung beim Zuschauer. Ob der aufgeht, zeigt sich ebenfalls erst im Schnitt.
Monatelang wird Geld in ein Drehbuch investiert, ohne dass man weiß, ob das Endresultat dem künstlerischen Anspruch gerecht wird. Wochenlang wird dann Geld in einen Dreh investiert, ohne dass man weiß, ob der in Summe das Drehbuch angemessen umsetzt und im Schnitt das gewünschte Ergebnis entstehen kann.

Während seiner ersten 10 Filme hatte Wim Wenders versucht, diese Unsicherheiten durch viel Planung zu kompensieren. Er hatte Drehbücher nicht nur umgesetzt, sondern auch geschrieben. Nicht umsonst war er Mitgründer des Filmverlags der Autoren. Damit vermied er Reibungsverluste zwischen Regisseur und Drehbuchautor. Seine Planung begann also schon bei der Idee.

Während des Drehs dann hatte er - wie er heute während eines Colloquiumgesprächs an der Hamburger Hochschule für Musik und Theater berichtete - alles genau vorausgeplant. Jede Einstellung, jeder Auftritt, jeder Abgang, alles hatte er spätestens am Vorabend eines Drehs genaustens ausgetüftelt. Damit sollten Reibungsverluste zwischen Schauspielern und ihm oder auch der Technik während des Drehs reduziert werden. Es galt ja, die knappe Drehzeit bestmöglichst zu nutzen.

So war das. Bis zum Film "Paris, Texas".

Da machte er es anders. Motiviert hatte ihn seine Erfahrung mit der Inszenierung eines Theaterstücks. Denn im Theater wird anders gearbeitet. Es gibt nur eine Einstellung, es gibt nur eine chronologische Darstellung. Und hat das Stück begonnen, muss der Regisseur auf die Schauspieler vertrauen. Während der ganzen Vorstellung kann er nicht mehr eingreifen.

"Paris, Texas" hat Wim Wenders dann anders gemacht.

Das Drehbuch war nur halb fertig bei Drehbeginn. (Eine zweite Hälfte existierte nur für die Geldgeber. Sie war nie für die Produktion bestimmt, sondern ein Fake.)

Und der Dreh verlief streng chronologisch.

Das Ergebnis? Ein Film der von der Kritik gelobt und mit der Goldenen Palme in Cannes ausgezeichnet. Das Ergebnis lässt sich also sehen - und das ganz ohne Aufhäufung von Halden. Für Wim Wenders ist klar, dass der Film seine Qualität nur erhalten konnte, weil eben nicht zwanghaft ein Ende vor Drehbeginn vorgedacht worden war und sich die Schauspieler mit ihren Figuren entwickeln konnten. Das Geheimnis für das gute Ergebnis, ja, das bessere Ergebnis als bei vorhergehenden Filmen besteht für ihn mithin in der Vermeidung von Halden. Er hat in nichts investiert, von dem er nicht wusste, ob es so später gebraucht werden würde. Er hat sich mit keiner Halde von Vorproduziertem die Möglichkeit zur Reaktion auf Entwicklungen während des Drehs eingeschränkt.

Mir scheint das eine kopierenswerte Haltung. Auch in der Softwareentwicklung sollten wir es vermeiden, ein ausgefeiltes Drehbuch von Anfang bis Schluss zu schreiben. Vor Beginn der Entwicklung zunächst ein Backlog satt zu füllen, versenkt Geld in Anforderungen, von denen nicht sicher ist, wann, wie und ob sie später umgesetzt werden sollten, ob sie einem Anwender wirklich den intendierten Nutzen bieten. Ein pralles Backlog schränkt durch die Investitionen auch die spätere Reaktionsmöglichkeit ein. Denn was im Backlog steht, muss ja umgesetzt werden. Immerhin steckt da ja einige Mühe drin. Die darf nicht umsonst sein. Wenn in Wochen oder Monaten Neues auftaucht, dann hat sich das hinten anzustellen. Mit einem ausführlichen Backlog gilt das Prinzip "First come, first serve".

Ebenso sollte die Softwareentwicklung vermeiden, Code auf Halde zu produzieren. Der nicht-chronologischen Drehreihenfolge beim Film entspricht eine Entwicklungsreihenfolge, die sich von Befindlichkeiten der Softwareentwicklung leiten lässt. Wenn die meint, es müsse zuerst eine Infrastruktur aufgesetzt werden, ein Persistenzframework geschrieben, ein Security-API entwickelt werden, bevor auch nur ein Anwender etwas in die Hand bekommt… dann produziert sie Code auf Halde. Von dem weiß auch niemand, wann, wie, ob er mal Nutzen entfalten wird.

Zum Glück vertritt die Agilitätsbewegung diese Sichtweise schon länger. Sie ermahnt, Code nicht auf Halde zu produzieren, sondern nur in nützlichen Inkrementen. Sie ermahnt, nicht zuviel in Vorabplanung von Anforderungen und Entwürfen zu investieren. In der Projektrealität ist das allerdings immer noch nicht überall angekommen. Deshalb finde ich es motivierend, aus ganz anderer Richtung davon zu hören, wie die Reduktion von Halden erfolgssteigernd wirken kann. Dass selbst die Filmproduktion davon profitiert… Wer hätte das gedacht?

Vielen Dank, Wim Wenders, für diese Plauderei aus dem Nähkästchen.


PS: Woody Allen hat übrigens auch erkannt, dass Drehbuchhalde und Takehalde sich abträglich auf das künstlerische Ziel auswirken können. Er stellt sie zwar noch komplett her - doch er erlaubt sich spätere Korrekturen. In den Verträgen seiner Schauspieler steht, dass sie - wenn ich es recht erinnere - bis zu einem halben Jahr nach Drehschluss noch für Nachdrehs zur Verfügung stehen müssen. Für Woody Allen kann sich einfach im Schnitt herausstellen, dass Haldenmaterial in seiner Qualität ungenügend ist. Dann bessert er aufgrund der gewonnenen Erkenntnisse einfach nach.
Das scheint mir auch in Linie mit der Agilität zu sein. Woody Allen hat erkannt, dass sich Ziel und/oder Weg mit der Zeit durch die Produktion verändern können. Das überrascht ihn nicht mehr, sondern er weiß es und trifft also vertragliche Vorkehrungen.

Donnerstag, 4. Oktober 2012

Gesucht: dreckige Realität

In einem Blogartikel hat Matthias Bohlen seinen Ansatz beschrieben, die Form eines Softwaresystems zu entwerfen. Leider hat mich die Lektüre unbefriedigt zurückgelassen.

Dass Matthias einen anderen Ansatz verfolgt als ich, erklärt das allerdings nicht. Warum sollte mich das auch stören? Der Entwurf hat mich ja nicht in frustriert-sprachloses Erstaunen versetzt. Also erwächst aus der Andersartigkeit seines Ansatzes eher energievolle Spannung. Ich werde seinem Entwurf auch einmal meinen gegenüberstellen. Dabei kann ich wieder für mich etwas lernen. Sie können für sich vergleichen. Und vielleicht ergibt sich daraus auch noch ein konstruktives Gespräch.

Nein, der Ansatz ist es nicht, der mich frustriert hat. Es war eher die Aufgabe. Aber auch wiederum nicht konkret die Bankendomäne, sondern die, hm, Präsentation und der Scope. Beides war so typisch. Nämlich typisch – sorry to say, Matthias – flach.

Ich habe es schon so oft gesehen in Büchern und Artikeln: Da wird eine Technologie oder Methode vorgestellt anhand eines Beispiels, bei dem es einfach funktionieren muss.

Bis zu einem gewissen Grad ist das auch legitim oder sogar nicht zu vermeiden. Auch ich habe schon so Technologien oder Methoden vorgestellt; manchmal liegt es an der Zeit, die zur Verfügung steht. Gerade deshalb bin ich da aber vielleicht auch sensibel. Ich möcht es besser machen für Sie und auch für mich.

Das Problem bei einer flachen Aufgabenstellung, bei Anforderungen, die eine Technologie oder Methode quasi zum Selbstgänger machen ist schlicht, dass man sich in die Tasche lügt. Das kann dem Präsentierenden selbst nicht gefallen. Und es ist natürlich unschön für die Leserschaft.

Ja, das ist es, das hat mich bei Matthias Lösung gestört. Das unselige, weil schon so oft zitierte Überweisungsszenario ist so trivial, dass sich damit ja fast jede Methode erfolgreich demonstrieren lässt. Ein “normal” objektorientierter Ansatz hätte dabei genauso sinnvoll dargestellt werden können wir ein prozeduraler oder ein funktionaler. Ich kann jedenfalls nicht erkennen, dass Matthias’ DCI-Ansatz einem anderen überlegen ist.

Damit will ich keine Aussage über seinen Ansatz machen. Bei der Größenordnung an Aufgabe macht er für mich schlicht keinen deutlichen Unterschied. Er funktioniert. Klar. So wie der “normale” objektorientierte Ansatz auch vorher funktioniert hat.

Was wir bei Matthias sehen ist eine Momentaufnahme eines kleinen Szenarios. Und wenn wir in ein Buch zur Objektorientierung oder zu DDD schauen, dann sehen wir dort auch Momentaufnahmen. Da hat jemand lange an Code gearbeitet, ihn fein ziseliert – und präsentiert das kunstvolle Ergebnis.

Nur ist so leider nicht die Realität. Die ist dreckig und hektisch und nicht so strukturiert in ihren Anforderungen. Außerdem sind die deutlich umfänglicher. Da gibt es Seiteneffekte. Da müssen nicht-funktionale Anforderungen berücksichtigt werden. Und da verändert sich so einiges über die Zeit.

Wir brauchen mehr Demonstrationen von Technologien und Methoden, die es damit aufnehmen. Auch und gerade, wenn es um Methoden geht. Denn ob eine Technologie funktioniert oder nicht, das lässt sich leicht selbst feststellen. Zur Laufzeit gibt es gewöhnlich blitzschnell Feedback. Doch ob eine Methode funktioniert bzw. ob man sie richtig anwendet, das ist viel schwieriger festzustellen. Womöglich stellt sich das erst nach Wochen oder Monaten heraus, wenn man sich damit in eine Ecke gepinselt hat.

Matthias’ Lösung kommt mir insofern “erschlichen” vor. Das Problem ist so abstrakt, isoliert dargestellt, dass sich quasi alles wie von selbst fügt. Erklärungen sind kaum nötig. Jeder nickt sofort, wenn da die Rollen aufgetischt werden… und jeder hält es dann für ganz natürlich, dass ein Konto das andere auffordert etwas zu tun.

Aber das kann doch nicht Matthias’ Ernst sein. In welcher Bankenwelt sollen denn Konten direkt miteinander sprechen? Auch wenn ich kein Banker bin, hat das für mich keinen Rückhalt in der Realität der Bankdomäne. Deshalb scheinen mir die Rollen Quellkonto und Zielkonto “erschlichen”. Wie bei einer petitio principii, wo das zu Beweisende vorausgesetzt wird. Oder so ähnlich wie die Antwort auf die Frage aus Ottos Quizparodie, wer denn der berühmte Maler in Rembrandts Haus sei.

Aber nochmal: Damit will ich Matthias´ Ansatz nicht per se kritisieren, sondern nur sagen, dass ich seinen Wert anhand des flachen Beispiels noch nicht erkennen kann. Wo die Anwendungsfälle so klein sind, dass Objekte und Rollen auf der Hand liegen, kann ich jede Methode rechtfertigen, die Objekte und Rollen favorisiert.

Um mich zu überzeugen braucht es also etwas Größeres und Dreckigeres. Etwas, das nicht schon für eine Methode analysegerecht kleingehackt ist.

Aus diesem Grund habe ich schon vor längerer Zeit in Anlehnung an die kleinen Code Katas größere Application Katas beschrieben. Einige finden sich hier: http://clean-code-advisors.com/ressourcen/application-katas. Technologisch sollten sie keine Herausforderungen darstellen. Aber methodisch kann man sich an ihnen abarbeiten. Deshalb liegen sie in mehreren Iterationen vor.

Wie passt darauf der Ansatz von Matthias? Wie flott segelt er durch diese Anforderungen? Wie leicht lässt sich eine DCI-Struktur über die Iterationen verstehen und verändern?

Diese Fragen gelten aber natürlich nicht nur für Matthias. Jeder, der eine Methode für die Softwareentwicklung im Ärmel hat, kann sich daran probieren. “Normale” Objektorientierung genauso wie Funktionale Programmierung oder der de facto Stil eines Teams. Ich selbst demonstriere Flow-Design immer wieder an solchen Beispielen im Rahmen von Artikeln oder im Seminar “Agile Architektur” der Clean Code Developer Akademie.

Doch die AppKatas sind weit weg von Matthias’ Aufgabenstellung. Da mag es schwer fallen zu sehen, was ich mit “mehr dreckige Realität” meine. Deshalb hier meine Version seines Überweisungsszenarios, als ganze Anwendung mit einem UI und persistenten Daten.

Application Kata - Banküberweisung

Auch das ist natürlich immer noch eine Spielzeugvariante dessen, was in Banken tatsächlich läuft. Aber es sind zumindest mehr Facetten darin enthalten, so dass es schwieriger wird, seinen Entwurfsweg zu finden. Die Entscheidungen sind nicht vorgekaut. Ein wie immer geartetes Modell – um das es ja beim Entwurf geht – liegt nicht auf der Hand.

Also, das ist eine Art von Herausforderung, wie ich sie häufiger formuliert und dann angegangen sehen möchte. Alles andere ist Spielkram.

 

PS: Wer sich beklagen möchte, in der Aufgabenstellung seien zu viele Dinge nicht ausformuliert und deshalb sei es schwierig zu entwerfen, der kann gern in den Kommentaren hier Fragen stellen. Dann konkretisiere ich als “Kunde” :-)