Follow my new blog

Sonntag, 30. Mai 2010

Coding Dojo Muc Retrospektive

Softwareentwicklung mit Energie – die war wieder mal zu sehen beim Coding Dojo in München. Knapp 30 Entwickler waren zusammengekommen, um sich in geselliger Runde in der .NET Programmierung anhand einer Aufgabe zu üben.

Das Coding Dojo Muc hat sein Format und seinen Rhythmus. Im Fokus steht gewöhnlich die testgetriebene Entwicklung (TDD) einer Funktionalität, die sich mit einer Funktion als API ausdrücken lässt  (Beispiele: FizzBuzz, LOC Counter). Das Motto: Alles denkt, einer tippt.

Doch dieses Mal war vieles anders…

Der Ablauf

image Dieses Mal hatte Ilker mich eingeladen, um in das Dojo ein wenig architektonisches Denken hineinzutragen. Das Motto: Alles denkt, keiner tippt ;-) Zumindest nicht die ersten zwei Stunden.

Die Frage nun: hat das geklappt?

Die Aufgabe

Um architektonisches Denken zu üben, musste die Aufgabe natürlich etwas größer ausfallen als Dojo-gewöhnlich. Auf der anderen Seite durfte sie nicht zu groß sein, denn ich wollte ja auch mit Ergebnis mit den Teilnehmern erzielen.

Angemessen erschien mir schließlich eine Spellchecker-Anwendung: Das Programm sollte Texte in verschiedenen Sprachen auf Rechtschreibfehler überprüfen. Wenn man so ein Programm aufsetzen will, sind mehr als eine architektonisch relevante Funktionseinheit nötig; Architektur lohnt sich also.

Der Umfang dieser Aufgabe führte dann gleich zu einer ersten Neuerung bei Dojo: der Anforderungsanalyse. Nicht, dass bisher die Teilnehmer nicht versucht hätten, die Aufgaben zu verstehen. Aber so ein Programm machte eine systematischere Herangehensweise nötig. Die bestand aus zwei Schritten:

1. Featureliste

image Im ersten Schritt haben wir die Anforderungen mündlich ventiliert. Da kamen einige Fragen an den “Kunden” zusammen. Und der Kunde blieb keine Antwort, keinen Wunsch schuldig – allerdings konnte nicht alles wirklich in das Backlog übernommen werden. Wir hatten ja nur einen Abend für die Realisierung zur Verfügung. Nichtsdestotrotz war die Diskussion wichtig, um die Gruppe auf das Thema einzuschwingen.

Was am Ende dann realisierbar erschien, haben wir in eine Featureliste übertragen. Wichtig dabei: Nur aus Kundensicht Relevantes ging darin ein.

2. Ubiquitous Language

image Die Featureliste hat den generellen Scope der Entwicklung abgesteckt. Sie hat eine Grenze gezogen. Wie sah aber das Terrain innerhalb dieser Grenze aus? Wie sollten Problem und Lösung zusammenfinden?

Um einen Übergang in die Lösungswelt zu finden, haben wir im zweiten Schritt für die Features eine Ubiquitous Language mittels einer Concept Map erarbeitet.

Darin wurden die zentralen Begriffe der Domäne nicht nur gelistet, sondern auch qualifiziert in Beziehung gesetzt. Interessante Erkenntnisse wurden dadurch herausgearbeitet:

1. Um sich unzweideutig über die Domäne unterhalten zu können, ist zwischen Prüfworten und Fehlerworten zu unterscheiden. Prüfworte sind die, die auf Korrektheit geprüft werden müssen; Fehlerworte sind die, die als Inkorrekt erkannt wurden.

Fehlt die Unterscheidung, wird die Rede über die Domäne im besten Fall mühsam, im schlimmsten Fall jedoch Missverständlich.

2. Nachdem das Flipchart schon fast voll war, stellte sich die Frage, wer denn überhaupt die Rechtschreibprüfung vornehmen würde. Ihre Elemente (z.B. Prüfworte, Wörterbuch) waren vorhanden, es fehlte aber eine Instanz, die damit umging.

Diese Instanz war in der Diskussion immer implizit vorhanden. Das Team hatte sich oder das ganze Programm dafür in der Verantwortung gesehen. Das ist aber natürlich ungenügend, weil trivial bzw. impraktikabel. Es gilt vielmehr: Zentrale Domänenaufgaben müssen als “Bubbles” in einer Concept Map der Ubiquitous Language auftauchen.

Und so wurde spät aber doch der Prüfer als Vokabel in die Ubiquitous Language aufgenommen. Er prüft, ob ein Wort im Wörterbuch und damit korrekt oder als Fehlerwort zu melden ist.

Die Architektur

Nach soviel Vorgeplänkel waren die Teilnehmer natürlich gespannt darauf, wie denn aus den Anforderungen eine Architektur entstehen würde.

Einige wollten mit Klassendiagrammen anfangen. Andere wollten Schichten übereinander malen. Doch ich hatte mich entschieden, eine Architektur “nach der neuesten Mode” zu versuchen. Event-Based Components (EBC) sollten es sein.

Da keine Zeit für lange Erklärungen des Konzepts sein würde, fand ich mich mit dem EBC-Ansatz recht mutig. Aber es half nichts: Ich wollte einfach nicht mehr überholte Ansätze wie IBC (Injection-Based Components) widerkäuen. Viel simpler wäre es auch nicht nicht geworden. Womöglich sogar schwieriger, weil sich Abstraktionsniveaus mit IBCs schlechter modellieren lassen.

Also bin ich mit einem System-Umwelt-Diagramm eingestiegen und habe dessen System dann als “Hauptplatine” einer EBC-Architektur verfeinert und bin dann nochmal eine Ebene tiefer in den SpellChecker (SC) hinunter gestiegen:

  

In drei Abstraktionsschritten von den Anforderungen zur Architektur. Das war sicherlich überraschend für einige Teilnehmer.

Interessanterweise gab es aber weder dazu noch zu den “komischen Pfeilen”, die ja nicht wie üblich Abhängigkeiten, sondern Nachrichtenflüsse darstellen, grundsätzlich kritische Kommentare. Puh. Das hat mich erleichtert.

Die Kontrakte

Nach soviel Gerede und soviel Malerei stand dann zum Glück aber etwas Code auf dem Programm. Es galt, die die Kontrakte der ermittelten Komponenten festzuzurren. Trotz EBC bleibt Contract-First Design zentral für eine Architektur, die in der Implementierung nicht erodiert.

Für die Kontrakte (und den weiteren Code) hatte ich schon ein Google Project aufgesetzt und mit einer Visual Studio Solution ausgestattet. In die haben wir die Servicekontrakte für die Funktionalität und die Nachrichtenkontrakte “hineingegossen”:

image

Je Komponente (Bauteile und Platinen) ein Interface. Interface-Abhängigkeiten gibt es nicht – das ist ja der Trick bei EBCs.

Hier ein Beispiel für einen Servicekontrakt:

image

Und hier ein Teil des Nachrichtenkontrakts:

image

Die Vokabeln der Ubiquitous Language tauchen konsequenterweise darin wieder auf.

Die Implementation

Nach mehr als 2,5 Stunden war es dann soweit. Die Teilnehmer konnten in die Tasten greifen. Vier kleine Teams gingen daran, die Bauteil-Komponenten zu implementieren. Auch Gastgeber Ilker ließ es sich nicht nehmen, mitzumachen.

Anders als üblich arbeiteten die Teams allerdings still. “Entertainment” hatte es genug gegeben. Jetzt sollte nicht mehr lange (öffentlich) diskutiert werden. Gummi musste auf die Straße.

Um aber nicht nur Gummi bei der Implementation, sondern auch bei der Integration auf die Straße zu bekommen, schlug ich vor, zunächst nur Minimalfunktionalität ohne Tests zu implementieren. Das versetzte mich in die Lage, die Platinen zu prüfen.

Also machte der USB-Stick ein erstes Mal die Runde, um den Teams die Kontrakte zuzuspielen. Und dann ein zweites Mal die Runde zurück zu mir, um ihre rudimentären Implementationen abzuliefern. Darauf aufbauend war meine Aufgabe, die Hauptplatine und die SpellChecker-Platine zu implementieren und alles mit einer kleiner Host-EXE zu starten.

Anschließend gaben die Teilnehmer alles, um ihre Implementationen auszufleischen.

Um 22:17h war es dann soweit: Die Anwendung lief. Texte konnten gegen ein englisches und ein deutsches Wörterbuch geprüft werden. Dabei holperte es noch etwas im UI und die Wörterbücher waren mager – aber “der Kunde” konnte sehen, dass seine Anwendung lief.

Reflexion

Unterm Strich war das Coding Dojo ein Erfolg. Ich bin grundsätzlich zufrieden mit dem Ergebnis. Auch wenn es gedauert hat und wir über Stolpersteine gelaufen sind, gab es am Ende eine lauffähige Anwendung, die von einem verteilten Team realisiert worden ist.

Was ist gut gelaufen?

Ich würde sagen, dass das grundsätzliche Vorgehen gepasst hat. Anforderungen verstehen, Features aufschreiben, Context Map, Kontrakte implementieren: der Fluss war gut. Es gab allerdings Kritik, dass ich dabei zuviel angeleitet hätte. Diese Kritik kann ich gut nachvollziehen, doch ich sehe nicht, wie es hätte anders laufen können.

Wenn bei sonstigen Dojos nach 3 Stunden eine Funktion realisiert ist, dann wäre bei einer ganzen Architektur am Ende kaum etwas Lauffähiges herausgekommen. So schön eine gemeinsame Entwicklung durch die Teilnehmer gewesen wäre – sie hätte nicht mehr als ein gutes Erlebnis produziert. Da bin ich mir sicher. Der Grund: Wenn schon bei der Implementation einer Funktion die Meinung weit auseinandergehen, dann ist das noch mehr der Fall beim Thema Entwurf. Die unterschiedlichen Ansätze, ein leeres Flipchart-Blatt zu füllen, sind dafür der Beweis.

Ziel war nicht, die Gruppe “irgendwie” zu einem Ergebnis kommen zu lassen, sondern einen Weg systematisch zu beschreiten. Das bedarf der Anleitung.

Ich behaupte nicht, dass der Weg, den ich durch den Architekturdschungel “vorgetrampelt” habe, der beste ist. Aber ich behaupte, dass er lohnenswert war, einmal erfahren zu werden. Ich bezweifle, dass viele von den Teilnehmern in ihren Projekten je erlebt haben, wie nach einer sauberen Planung echt parallel und ohne Codekollisionen entwickelt werden kann. Das zu demonstrieren, war mein Anliegen.

Für eine “Diskussionskuschelrunde” mit etwas Architektur hätte Ilker mich auch nicht einladen müssen.

Bei einem nächsten Übungsprojekt wäre es möglich, mehr Arbeit ins Team zu verlagern. Dann würde sich zeigen, ob es etwas vom Vorgehen mitgenommen hat. Bei diesem ersten Mal war jedoch Anleitung in Form eines “fragend entwickelnden Unterrichtsgesprächs” und einem Architekturparadigma nötig.

Gefreut hat mich auch, dass die Concept Map so gut aufgenommen wurde und die Ubiquitous Language sich selbst bei diesem kleinen Projekt als hilfreich herausgestellt hat. Ich hatte mich zunächst gefragt, ob der Aufwand lohnt – doch jetzt bin ich gewiss. Die Concept Map sollte nie fehlen.

Basierend auf der Concept Map habe ich dann auch die Nachrichtenkontrakte gestaltet. Darin kamen im Grunde keine einfachen Typen mehr vor. Statt eines string hat das Frontend ein PrüfTextPaket geschickt. Das hat niemand als Overkill bemängelt. Sehr schön. Ich fühle mich in dem Ansatz bestätigt, zumindest auf Komponentenebene im Grunde keine primitiven Typen mehr einzusetzen. Man vergibt sich mit primitiven Typen nämlich ein Mittel, den Code verständlich zu machen. Aber dazu mehr in einem anderen Blogartikel.

Cool war es zu sehen, wie motiviert alle mitgedacht haben. Soviel Offenheit und Durchhaltevermögen bei Architekturdiskussion! Toll! Und dann auch zu später Stunde noch viel Ehrgeiz bei der Implementation. Wahnsinn!

Was ist weniger gut gelaufen?

Bis zur Implementation ist alles nach Plan gelaufen. Doch dann… Zwar hatte ich den richtigen Gedanken, Integrationsproblemen mit einer “Tracer Bullet” “Kurzimplementation” vorzubeugen. Aber die Ausführung habe ich schlecht vorbereitet.

Das Problem war nicht so sehr, dass Code nur per USB-Stick die Runde machen konnte. Schöner wäre zwar gemeinsamer Zugang mit Mercurial Repository gewesen… doch es ging auch so. Nur langsamer. Viel schlimmer war, dass ich zuwenig über Konventionen gesprochen habe. Denn wo Tools fehlen, müssen Konventionen her.

Die binären Kontrakte haben natürlich die grundsätzliche “Zusammensteckbarkeit” der getrennt entwickelten Komponenten garantiert. Das hat funktioniert. Wir hatten kein wirkliches Integrationsproblem der Assemblies.

Wie der Code aber an mich ausgeliefert wurde, das war ein Problem. Visual Studio kann ja in so unterschiedlicher Weise benutzt werden. Da habe zuwenig erklärt bzw. vorgegeben. Besser wäre es gewesen, ich hätte für jedes Komponenten kurz eine Rumpf-Projektmappe aufgesetzt. Das hätte zwar etwas Zeit gekostet – am Ende wäre dadurch aber so mancher Reibungsverlust vermieden worden.

Lehre #1: Solange Konventionen für die Codeorganisation unbekannt sind oder noch nicht verlässlich eingehalten werden, sollten Komponentenimplementationen als Rahmen vorgegeben werden. Dann gibt es keine Frage, wohin der Output-Path zeigen soll, woher NUnit kommt, wo die Tests zu hinterlegen sind, wie die Bennungen sind, so dass der autom. Buildprozess schlank bleibt.

Kreativität ist ja eine gute Sache – aber sie sollte einen Rahmen haben. Dass z.B. Entwickler die Freiheit haben, Komponenten nicht gem. der Ubiquitous Language zu benennen, ist falsch verstandene Kreativität.

Lehre #2: Die Ubiquitous Language ist heilig. Spaß mit Benennungen mögen Entwickler vielleicht hier und da in ihren Komponenten haben. An den Schnittstellen jedoch und auf Architekturebene ist dafür kein Raum. Verständlichkeit entsteht nur, wenn die gemeinsame Sprache auch wirklich gesprochen wird.

Die Zeit war am Ende knapp. So haben wir alle auf eine erfolgreiche Integration gestarrt. Darunter hat die Codequalität gelitten. TDD im Speziellen war dieses Mal nicht wirklich ein Thema. Und Clean Code Developer Bausteine im Allgemeinen (jenseits der Architektur) auch nicht. So haben sich – wie ein Blick in den Code zeigt – Unschönheiten eingeschlichen. Manche davon waren sogar auf der Projektion zu sehen, weil sie mit dem GUI zu tun hatten.

Lehre #3: Keine Integration ohne Code-Review. Soviel Zeit muss sein. Für den Event war es ok, mal ohne Review zu leben. Es ging ja vordringlich um den Architekturentwurf. Die Implementierung am Ende war Kür und Spaß. Wenn es jedoch etwas ernster wird, dann gibt es kein “Done!” ohne Review.

TDD erleben, ist eine gute Sache. Architekturen mit “parallel rauchenden Köpfen” umsetzen, ist aber auch eine gute Sache. Beides sollten Entwickler öfter tun. Für TDD mag dabei eine Projektion reichen. Beim parallelen Entwickeln ist jedoch erstens ein Coderepository und zweitens ein Zugriff für alle darauf unverzichtbar.

Lehre #4: Das Google Projects Repo war eine gute Sache  - allerdings nur für mich. Das sollte beim nächsten Mal anders sein. Wie könnte das gehen? Mehr Teilnehmer mit einem UMTS-Stick. Oder ein WLAN mit einem Rechner, der ein Repo hostet. Oder ich muss einfach auf dem Zettel haben, dass ich ja einen WLAN UMTS Stick habe, über den ich auch anderen Internetzugang geben kann. Die 4 Rechner hätte der wohl stemmen können.

Schwieriger wird es bei der Versionskontrollsoftware. Viele sind mit SVN oder TFS vertraut. Mit denen würde ich soetwas aber nicht machen wollen. Ein verteiltes Versionskontrollsystem wie Mercurial (oder auch Git) ist viel besser geeignet für verteilte Teams. Da kann jedes Team z.B. in mehreren Commits bis zur Tracer Bullet Implementation kommen, die mit einem Push auf den Server schieben und dann lokal weiterarbeiten, während die Integration sich den Tracer Bullet Stand von allen mit Pull herunterlädt.

Beim nächsten Mal versuche ich es mit TortoiseHg für alle. Das ist einfach zu bedienen. Damit bekommt jeder ein Clone, ein Commit und ein Push hin. Insbesondere ich Mercurial auch sehr tolerant gegenüber Löschungen und Umbenennungen.

Fazit

Trotz einiger Stolpersteine ist das Coding Dojo aus meiner Sicht schön gelaufen. Mein Eindruck war, die Teilnehmer haben etwas mitgenommen. Und sie haben auch Spaß gehabt. Sonst wären sie nicht bis zum bitteren Ende nach knapp 4 Stunden geblieben. Super! Danke an alle fürs Kommen und Mitdenken und Mitmachen.

Und Danke an Ilker für die Einladung. Und Danke an Microsoft für Raum und Annehmlichkeiten. Von mir aus können wir das gern wieder mal machen ;-)

Wem es Spaß gemacht hat und wer tiefer eintauchen möchte in systematischen Softwareentwurf und komponentenorientierte Entwicklung, der kann ja mal überlegen, ob er/sie beim Clean Code Developer Praktikum im Juli mitmachen will. Das bietet 5 Tage Praxis pur – und ist auch noch kostenlos.

Montag, 24. Mai 2010

Überraschende Relevanzwellen [OOP 2010]

image Schon interessant, was zu Wellen im Twitter-Teich führt. Da stolpere ich während der Lektüre von “Projektmanagement” von Thorsten Reichert beim “Zusammenfassungsservice” getAbstract über den Satz

„Viele Unternehmen haben nie Zeit, Dinge richtig zu tun, aber stets Zeit, Dinge mehrmals zu tun.”

Den twittere ich – und erzeuge unerwartet weitreichende Reaktionen. Wow!

Nicht, dass mich die Funktionsweise von Twitter wundert. Nein, mich wundert, was die Gemüter hier in Resonanz versetzt. Ist nicht mein erster Tweet, aber der erste, bei dem ich aus so unterschiedlichen Ecken Retweets sehe. Das Thema schlägt offensichtlich einige Saiten in der Entwicklergemeinde an.

Was sind denn typische Beispiele, wo das DRY-Prinzip vom Management nicht eingehalten wird? Doppeltarbeit statt “einmal richtig tun”? Wer mag Situationen und Fälle benennen? Je mehr Konkretes zusammengetragen wird, desto eher ists möglich, daraus Ursachenmuster abzuleiten und vielleicht doch mal etwas dagegen zu tun. In kleinen Schritten.

Freitag, 21. Mai 2010

Biosphere 2 - Sind Wissensinseln “böse”? [OOP 2010]

image Grad kommt alles zusammen. Ich lese einen Thriller über die Evolution, die auf einer abgeschiedenen Insel zu sehr bösen Kreaturen geführt hat (“Biosphere” von Warren Kaye). Dann höre ich Jens Coldewey auf der SET in Zürich, der darüber spricht, wie böse es für Projekte ausgehen kann, wenn sie sich nicht gegen Inseln wehren, gegen Wissensinseln.

Und dann spreche ich am selben Tag mit Ivan Engler, dem Regisseur des Sci-Fi Spielfilms “Cargo”, der kein Problem mit Wissensinseln zu haben scheint. Im Gegenteil! Sie machen für ihn einen Teil der Effizienz einer Spielfilmproduktion aus.

Ja, was denn nun? Sind (Wissens)inseln “gut” oder “böse”?

Natürlich gibt es da keine pauschale Antwort. Es kommt immer darauf an, auf den Kontext. Deshalb könnte ich das Thema auch zu den Akten legen und einfach sagen: Beim Film sind Wissensinseln ok, bei der Softwareentwicklung hingegen sind sie unakzeptabel. So ist das halt.

Doch ich mag mich noch nicht von den Wissensinseln in der Softwareentwicklung verabschieden. Der Begriff suggeriert Kontraproduktivität – aber den kann ich ja austauschen. Wenn ich stattdessen Spezialisierung sage, dann ist die Konotation schon ganz anders.

Wenn Wissensinseln pauschal “böse” sein sollten, dann ist Spezialisierung pauschal “gut”. Spezialisierung ist schließlich das Ergebnis einer komplizierten Welt, in der nicht mehr jeder alles können kann (oder will). Damit meine ich nun nicht die seelenlose Spezialisierung eines seiner Produkte entfremdeten Fließbandarbeiters. Nein, ich meine eine Spezialisierung wie die einer Maskenbildnerin oder eines Kameramannes oder eines Motoringenieurs oder eines Kommunikationselektronikers.

imageIvan Engler hat keine Angst, dass eine Maskenbildnerin krank wird. Sie ist auch in einer laufenden Filmproduktion in ihrer Funktion ersetzbar. Das mag nervig sein, weil dann Aufwand zu treiben ist für eine Ersatzkraft, doch das ist kein Beinbruch. Und auch VW hat keine Angst, dass ein Motoringenieur ausfällt. Ist der eine krank, übernimmt ein anderer diese hochspezialisierte Aufgabe.

In der Softwareentwicklung allerdings geht die Angst um. Der truck factor scheint quasi immer bei 1 zu sein, sobald sich eine Spezialisierung entwickelt. Das scheint mir der Befindlichkeit in vielen anderen Branchen zu widersprechen. Ich ahne eine Überempfindlichkeit der Softwareentwicklung.

Natürlich kann ich Jens Coldeweys grundsätzlichen Gedanken zustimmen. Es gibt Wissensinseln, die “böse” sind. Und man sollte sie sich auch nicht unbewusst entwickeln lassen. Praktiken wir Reviews und Pair Programming sind probat, um dem entgegen zu wirken. Kein Problem. Ich bin für diese Praktiken und Wissensaustausch im Team.

Und dennoch… Ich bin auch weiterhin der Überzeugung, dass es illusorisch ist anzunehmen, alle Entwickler könnten gleichermaßen Experten in all den Technologien sein, die in einer größeren Anwendung zum Einsatz kommen. Alle sind Silverlight-, WCF-, NHibernate-, WF-, Azure-, InRule-, Oracle-Profis usw.? Das ist Quatsch.

Im Umkehrschluss bedeutet das: Solange der Wille besteht, alle auf dem gleichen Level zu halten, kann dieses Level nur mittelmäßig at best sein.

Soweit die beiden Positionen: Wissensinseln sind “böse”, Spezialisierung ist für Höchstleistung unvermeidbar.

Lässt sich da noch eine Brücke schlagen?

Ja, mir scheint, das geht. Dazu müssen wir aber die Architektur in den Blick nehmen. Auch Jens Coldewey hat Conway´s Law erwähnt: Organisationsstrukturen spiegeln sich in Produktstrukturen wider. Da hake ich mal ein und frage: Wenn denn nun die Organisation dazu führt, dass eben keine Grenzen mehr zu sehen sind – alle sind in ihrem Wissen gleich –, wie soll denn dann die Software aussehen, die so eine Organisation produziert?

Vor dem Hintergrund von Conway´s Law bezweifle ich, dass eine Organisation, in der alle gleich sein sollen, Software herstellen kann, die wahrhaft modular ist, d.h. in der es ganz klare Verantwortlichkeiten gibt. Collective Code Ownership widerspricht insofern sauberer Architektur. Oder anders: Collective Code Ownership tut nichts dafür, dass es zu einer evolvierbaren Architektur durch Grenzziehung in Form von Entkopplung kommt. Wenn die hergestellt werden soll, dann muss ganz bewusst an anderer Stelle Kraft aufgewandt werden. Es gibt aus der Haltung zur Vermeidung von Wissensinseln heraus kein Bedürfnis, in Komponenten (oder gar “Services”) zu denken.

Und jetzt die Umkehrung: Ich setze voraus, dass Architektur modular (komponentenorientiert, serviceorientiert, entkoppelt usw.) sein soll. Punkt. Daran geht für mich nichts vorbei. Soviel ist der Langlebigkeit von Software geschuldet. Es geht ums pure Überleben des Produktes. Das bedeutet – im Sinne von Conway´s Law –, dass die Organisation ebenfalls “irgendwie” modular sein muss. Andernfalls reibt sie sich immer wieder auf, um Strukturen herzustellen, die ihr nicht entsprechen.

Unverbrüchliche Architekturprinzipien sollen also die Organisation treiben!

Daraus folgt für mich, dass z.B. eine grundlegende Trennung von Infrastruktur und Domäne in der Organisation zu sehen sein sollte. Domänenlogik ist etwas ganz anderes als ein Frontend, die Persistenz, die Sicherheit, die Kommunikation usw.

Nun die Masterfrage: In welchem dieser Bereiche sind Wissensinseln ein Problem? Aus meiner Sicht nur bei der Domäne. Software wird sehr sensibel überall dort, wo Domänenlogik im Spiel ist. Und das waren auch die Beispiele, die Jens Coldewey gebracht hat. Domänenlogik ist so sensibel, weil die Experten, die sich mit Softwareentwicklung und (!) Domäne auskennen, so dünn gesät sind.

Das ist übrigens auch der Fall im Filmbusiness. Kameramann und Regisseur und Schauspieler sind nicht so leicht auszutauschen wie eine Maskenbildnerin oder ein Mikrofongalgenhalter. Jene sind mit dem Thema des Films, der Vision eng verwoben; das ist die Domäne einer Produktion. Alles andere ist “Beiwerk” – wichtig, aber ohne Domänenbindung. Maskenbildner etc. haben generische Kompetenz.

Übertragen auf die Softwareentwicklung meine ich daher, dass wir dahin kommen müssen, architektonische Prinzipien ernst zu nehmen. Alles, was mit Infrastruktur zu tun hat, ist domänenunabhängig von der Architektur so zu kapseln, dass es von Experten mit generischer Kompetenz realisiert werden kann. Das mögen – je nach Anwendung – 5%, 10% oder 30% der Code sein. Immer bleibt der Löwenanteil jedoch in der Domäne.

Dennoch ist das ein Gewinn. Es ist nun ganz klar, welche Funktionalität grundsätzlich ohne truck factor realisierbar ist. Denn fällt der Silverlight-Experte aus, wird der nächste angeheuert. Der braucht nur wenig Einarbeitung, weil er keine Ahnung von der Domäne haben muss. Das hochspezialisierte Wissen für optimalen Technologieeinsatz macht kein Wissensinselproblem, weil es breit verfügbar ist (oder zumindest sein könnte). Hier kommt es vor allem auf Erfahrung mit nicht-funktionalen Anforderungen an. Die sind aber quasi in jedem Projekt zu gewinnen.

Es bleibt übrig der Bereich der Domäne. Hier werden die Experten in jedem Team individuell herangezüchtet. Und tatsächlich darf es in Bezug auf die Domäne im Team keine Wissensinseln geben. Domänenwissen ist so breit wie möglich verfügbar zu machen im Team. Denn dort sitzen die quasi einzigen Experten weltweit.

Von Domänenexperten dann aber auch noch Hochleistungen in technologischer Hinsicht zu erwarten, halte ich für eine Überforderung. Natürlich darf, kann, soll jeder Domänenexperte über eine Domänenkenntnis und gute “Programmierkenntnisse” der Plattform hinaus auch noch technologische Spezialgebiete haben. Nur sollte sich ein Projekt darauf nicht verlassen. Und es sollte nicht versuchen, solche technologischen Kompetenzen breit zu streuen. Das funktioniert nicht.

Stattdessen ist es Aufgabe der Architektur, Spezialgebiete (oder Belange) deutlich zu trennen. Die Domäne ist ein solches Spezialgebiet, Persistenz, Frontend etc. sind andere. Und für jedes Spezialgebiet ist Redundanz in Bezug auf die Kompetenzen herzustellen.

Redundanz in der Domäne ist Aufgabe des Projekteams, Redundanz bei Infrastrukturtechnologiekompetenzen ist Aufgabe des Marktes.

Die Filmindustrie hat das hinbekommen. Maskenbildern, Tischler, Bühnenbildner gibt es zuhauf. Bei der Domäne ist es dort aber naturgemäß schwierig. Dafür sind die Projekte auch zu kurz und personengebunden. Deshalb gibt es Versicherungen. Fällt der Hauptdarsteller wegen Unfall aus, kompensiert eine Versicherung den Verlust.

Das könnte auch in der Softwareindustrie funktionieren – wenn denn ein Team nachweisen kann, dass es Wissensinseln vermieden hat, soweit das möglich ist. Aber das ist ein anderes Thema…

An dieser Stelle ich mir wichtig, die Vereinigung aus Spezialisierung und Vermeidung von Wissensinseln herzustellen. Die liegt darin, die architektonisch saubere Separation of Concerns auf das Team zu übertragen. Daraus folgt, dass das Team bzw. Kernteam vor allem die Verantwortung für die Domäne bekommt. Darin sollen dann alle im Team Experten werden; Wissensinseln sind zu vermeiden, weil es außerhalb des Teams kaum Ersatz gibt.

Alle anderen Belange sollte das Kernteam aber nicht auch noch exzellent abdecken. Stattdessen sollte es sie strukturell isolieren und von externen Experten aufsetzen lassen. Im Team muss dann nur noch  Pflegekompetenz existieren. Teammitglieder müssen damit arbeiten und nur noch verstehen, was da gemacht wurde und hier und da korrigierend eingreifen können. Falls tiefere Kenntnis nötig werden sollte, wird wieder ein Experte “eingekauft”.

Damit vermeiden Teams den unmöglichen Spagat zwischen hoher Domänenkompetenz auf der einen Seite und technologischer Exzellenz auf der anderen Seite. Und so entsteht auch ein Markt für technologische “Söldnerexzellenz", die öfter als bisher in unterschiedlichen Projekten eingesetzt wird.


Anders, so glaube ich, kommen wir nicht heraus aus dem ewig mittelmäßig bis schlechten Einsatz von Technologien. Wissensinseln sind also tatsächlich zu vermeiden; das bedeutet jedoch nicht, wir müssen auf eine Spezialisierung verzichten. Spezialisten sind nötig, dürfen nur nicht unersetzbar sein. Deshalb darf der Anspruch nicht sein, alle Spezialisten “selbst herzustellen”. Das ist die Herausforderung.

Natürlich ist es einfacher, “alle sollen möglichst gleich sein” zu denken. Das macht weniger Planungsaufwand. Und das ist auch ok. Nur darf ein Team dann nicht erwarten, wenn es in vielen Disziplinen “gleichgeschaltet ist”, in allen auch Höchstleistungen zu erbringen. Das Prinzip “Wissensinseln vermeiden” sollte daher nicht pauschal allein Anwendung finden.

image

image PS: Ivan Engler wird übrigens auf der prio.conference die Keynote halten. Zumindest wenn er dann nicht gerade in Hollywood weilen muss. Doch ich bin da mal zuversichtlich…

Montag, 10. Mai 2010

Kaffeehauskonsultation in München

Umfrage: Kaffee ist Teil einer ausgewogenen Ernährung Am Donnerstag 27.5.2010 ist es wieder so weit: kostenlose Beratung in Sachen .NET Softwareentwicklung in München. Von 10h bis 17h können Sie mich in meiner “Praxis” konsultieren. Die öffne ich im Café Mozart und bin gespannt, worüber wir uns unterhalten. Sie finden mich dort “im Hinterzimmer” für die Kaffeehauskonsultation.

Bringen Sie Ihren Laptop mit, dann können wir uns auch Ihren Code ansehen, falls nötig. Einen Block für Diagramme habe ich dabei. Und dann reden wir über Softwarearchitektur, Clean Code Developer und andere spannende Themen.

Sie können natürlich einfach so vorbeischauen. Besser ist´s jedoch, wenn Sie mir schnell eine Email schicken, damit wir einen Termin abmachen. Jedes Gespräch sollte nicht länger als 75 Minuten dauern, damit auch andere eine Chance auf Konsultation bei Kaffee haben.

image

Donnerstag, 6. Mai 2010

Asynchrone Kommunikation mit EBCs statt “Async-Pattern”

Freut mich, dass Thomas Christian im build broken Blog eine Lanze für die asynchrone Kommunikation mit Events gebrochen hat. Schade allerdings, dass der Code dafür so umständlich aussieht.

Kommunikation à la Async-Pattern

Ein kleines Formular fordert von einem Calculator die Quadratur einer Zahl an: m_calculator.CalcAsync(number). Was würden Sie dann im Calculator für Code erwarten? Irgendwo sollte number * number stehen. Klar. Und ansonsten? Schauen Sie einmal, wie der Calculator bei Thomas aussieht:

image
image

Puh… Da ist es gar nicht so leicht, das zu finden, worauf es eigentlich ankommt, finde ich. Sehen Sie die kalkulierende Businesslogik? Und was macht der Rest des Codes? Der ist dazu da, ein Pattern zu implementieren.

Wenig Code für das Wesentliche, viel für Infrastruktur – das hört sich für mich nicht so gut an, auch wenn ich das Kommunikationsmodell voll unterstütze.

Ich schlage daher einen anderen Weg vor. Hier meine Version der Szenarios:

Kommunikation à la Event-Based Components

Zunächst die Businesslogik:

image

Hier finden Sie keine Rauschen durch Infrastrukturcode. Der Fokus ist ganz klar auf der Problemdomäne. Aber Achtung: Ich habe keine Funktion für Calc() geschrieben, sondern eine Prozedur, d.h. eine Methode ohne Rückgabewert. Das Resultat liefert die Methode dann über einen Event: DeliverCalcResult(). So ist das bei Event-Based Components.

Jetzt der Client-Code im Formular:

image

Auch hier kein Rauschen durch Infrastruktur. Nicht mal der Calculator wird als Komponenteninstanz injiziert. Das Formular kennt keinen Calculator. Es ist völlig unabhängig von einer solchen Funktionseinheit. Es äußert Berechnungswünsche lediglich über einen Event – CalcRequest – und empfängt Berechnungsresultate mit OnCalcResult().

Das ist im Prinzip sehr ähnlich wie bei Thomas. Im Detail ist es jedoch anders, da das Formular eben auf die Kenntnis einer Service-Funktionseinheit verzichtet. Die wird bei Thomas noch im Formular instanziert.

Und wie kommt es nun zu Berechnungen, wenn das Formular den Calculator nicht kennt? Beide Funktionseinheiten werden von außen verdrahtet. Damit sind Logik und Abhängigkeiten als Concerns sauber getrennt.

image

Voilà! Jetzt kann das Programm starten und berechnet Quadratzahlen. Zuerst werden die Funktionseinheiten instanziert. Dann werden die Eventhandler in die Events gesteckt. Mehr ist nicht nötig. Keine Funktionseinheit weiß von der anderen. Nur der Verdrahtungscode kennt sie.

Asynchrone Kommunikation

Die kleine Anwendung funktioniert nun zwar, aber synchron. Thomas Beispiel will ja aber gerade zeigen, wie einfach asynchrone Kommunikation mit Events ist. Was muss ich also tun, um meinen Nachbau ebenfalls asynchron zu machen?

Asynchronizität erfodert keine (!) Veränderung an der Geschäftslogik oder im Frontend. Das halte ich für den wesentlichen Vorteil meines Vorgehens. Ich muss lediglich die Verdrahtung anpassen:

image

Ich instanziere zwei kleine Standard-Funktionseinheiten, eine, die Events asynchron weiterleitet und eine, die Events zurück in den GUI-Thread bringt. Diese Funktionseinheiten schalte ich zwischen den Dialog und den Calculator. War die Verbindung z.B. bisher direkt

dlg.CalcRequest = calc.Calc;

so ist sie nun indirekt:

dlg.CalcRequest = asyncProcessAsynchronously;
async.OnAsynProcessing = calc.Calc;

Die Standard-Komponenten sind natürlich auch Event-Based Components, d.h. Input erhalten sie über Eventhandler und Output generieren sie mittels Events.

Das war´s. Mehr ist nicht zu tun. Die Geschäftslogik läuft jetzt im Hintergrund. Und die Ergebnisse kommen problemfrei im Vordergrund an. Wie das funktioniert? Egal :-) Das ist die Magie der Standard-Komponenten.

Fazit

Asynchrone Kommunikation mit Events ist eine gute Sache, da stimme ich Thomas zu. Aber man sollte diese Sache dann auch so einfach wie möglich halten. Das bedeutet für mich, dass ich in der Geschäftslogik (oder auch im Frontend) von technischen Klimmzügen, die die Asynchronizität herstellen, nichts sehen will. Geschäftslogik muss frei von diesem Concern sein. Der steckt am besten in wiederverwendbaren Standard-Komponenten. Event-Based Components bieten dafür eine gute Grundlage.

P.S.

Na, schön. Hier nun doch noch der Code für die Standard-Komponenten. Damit keiner sage, der sei bestimmt total kompliziert ;-)

image

image

Dieser Code ist wiederverwendbar. Er folgt nur dem Event-Based Component Muster für die Kommunikation. Deshalb kann er mit beliebigen Pins von anderen Komponenten zusammengesteckt werden, ohne dass die etwas davon merken. Das ist Composability.

Montag, 3. Mai 2010

Nullonade – Monade statt Null

In der Diskussion um Null als Rückgabewert gab es ein Codeschnippsel, mit dem Thomas Bandt zeigen wollte, dass der TryGetUserById(…)-Vorschlag nicht zwangsläufig zu intuitivem Code führt. Ich formuliere das Beispiel hier mal etwas knapper:

User tmpUser;
bool isAuth = repo.TryGetUserById("987", out tmpUser) &&
              sec.CheckUser("somepassword", tmpUser);

Als Problem sieht Thomas – durchaus zurecht – die Abhängigkeit von CheckUser() von einem Seiteneffekt. Die globale Variable tmpUser muss vor Aufruf gesetzt sein, was innerhalb des logischen Ausdrucks nicht ganz so offensichtlich ist.

Etwas deutlicher wäre die Abhängigkeit der Reihenfolge beider Methodenaufrufe, wenn der Ausdruck traditioneller als Schachtelung formuliert wäre:

bool isAuth;
User tmpUser;
if (repo.TryGetUserById("987", out tmpUser))
    isAuth = sec.CheckUser("somepassword", tmpUser);
else
    isAuth = false;

Doch eigentlich sind Schachtelungen ja “böse” ;-) Sie erhöhen die (zyklomatische) Komplexität des Codes. Also vielleicht lieber so:

User tmpUser;
bool isAuth = repo.TryGetUserById("987", out tmpUser);
isAuth = isAuth && sec.CheckUser("somepassword", tmpUser);

Das mag besser lesbar sein – doch das Grundproblem ist eigentlich nicht gelöst. Das besteht in der Abhängigkeit selbst, also letztlich in der zu beiden Methoden globalen Variablen tmpUser. Die ist nötig, weil Thomas keine Exception werfen möchte, falls es den angeforderten User nicht gibt. Wäre das anders, könnte er nämlich auch ohne diese Variable formulieren:

bool isAuth = sec.CheckUser(“somepassword”,
                            repo.GetUserById(“987”));

Das ist zwar wieder geschachtelter Code, doch irgendwie sind wir sowas ja gewohnt ;-)

Get into the flow

So richtig cool finde ich das aber alles noch nicht. Deshalb schlage ich mal eine weitere Alternative vor. Wäre es nicht am allerschönsten, wenn wir es so schreiben könnten:

var isAuth = repo.GetUserById(“987”) |> sec.CheckUser(“somepassword”);

Das ist natürlich Pseudocode. Aber schön wärs, oder? GetUserById() würde immer noch keine Exception werfen und trotzdem (!) würde das Ganze immer funktionieren.

In F# geht das. Da gibt es so einen Pipe-Operator |> und noch etwas anderes, das nötig ist, einen Option-Typ. Aber in C# sitzen wir erstmal auf dem Trockenen :-(

Zum Glück können wir daran etwas tun. Wir können mit etwas Vorarbeit auch in C# so einen “Flow” von Anweisungen schreiben. Hier meine C#-Version:

bool isAuth = repo.GetUserById("987")
                  .Continue(user => sec.CheckUser("somepassword", user))
                  .WithDefault(false);

Liest sich doch gar nicht schlecht, oder? Es soll ein User geladen werden und danach soll geprüft werden, ob der User ein bestimmtes Passwort hat. Ist das so, dann ist das Ergebnis true, ansonsten false.

Die Sequenz der Verarbeitungsschritte in unserem Kopf findet sich im Code wieder – ohne dass wir eine temporäre Variable bräuchten. Naja, nicht ganz: denn der user-Parameter der Lambda-Funktion ist eine solche temporäre Variable. Doch die ist nicht global, sondern lokal. Es gibt also keine schwer zu überblickenden Abhängigkeiten.

Optionen als Zwischenwerte

Möglich ist so eine Formulierung, wenn als Resultat von GetUserById() kein User (oder gar Null), sondern ein sog. Option-Wert zurückgeliefert wird. Den gibt es in F# gratis; für C# können wir ihn so bauen:

public class Option<T>
{
    public Option()
    {
        this.IsSome = false;
    }

    public Option(T some)
    {
        this.Some = some;
        this.IsSome = true;
    }

    public T Some { get; private set; }

    public bool IsSome { get; private set; }
    public bool IsNone { get { return !IsSome; } }

    …
}

Der Trick an Optionswerten ist, dass es immer eine Instanz gibt. Es geht also nicht um Objekt oder Null, sondern um Options-Objekt gefüllt oder nicht gefüllt. Wenn es gefüllt ist, dann liefert IsSome true, wenn nicht, dann liefert IsSome false bzw. IsNone true. Zugriff auf den Wert gibt die Some-Property.

var optInt = new Option<int>(42);
Console.WriteLine(“{0}, {1}”, optInt.IsSome, optInt.Some);

gibt aus:

true, 42

Mit so einem Typen kann GetUserById() nun umformuliert werden zu:

Option<User> GetUserById(string id) {…}

Die Methode liefert immer ein Option-Objekt zurück und die Umgebung muss prüfen, ob etwas drin steckt oder nicht:

var optUser = repo.GetUserById(“987”);
if (optUser.IsSome)
    …

Das ist fast so wie eine Prüfung auf Null – aber besser. Denn mit einem Option-Objekt kann man immer weiterarbeiten.

Logik verstecken in einer Monade

Das Ziel ist immer noch der “Flow”, d.h. globale Variablen und Prüflogik auf unerwünschte Ergebnisse zu verstecken. Weil nun immer ein Option-Objekt vorliegt, können wir darauf immer eine Methode aufrufen

public class Option<T>
{
    …

    public IEnumerable<Option<TOutput>> Continue<TOutput>(Func<T, TOutput> processor)
    {
        return new[] {this}.Continue(processor);
    }
}

und auch noch Erweiterungsmethoden in Anschlag bringen

public static class OptionExtensions
{
    public static IEnumerable<Option<TOutput>> Continue<TInput, TOutput>(
                                     this IEnumerable<Option<TInput>> values,
                                     Func<TInput, TOutput> processor)
    {
        return values.Where(v => v.IsSome)
                     .Select(v => new Option<TOutput>(processor(v.Some)));
    }

    public static TInput WithDefault<TInput>(
                                     this IEnumerable<Option<TInput>> values,
                                     TInput defaultInCaseOfNone)
    {
        return (values.FirstOrDefault() ??
                     new Option<TInput>(defaultInCaseOfNone)).Some;
    }
}

Ich denke, damit habe ich eine Monade definiert, d.h. ein “Dings”, mit dem sich quasi unsichtbar ein Kontext um mehrere Anweisungen wickeln lassen kann.

Dass ich dafür auf IEnumerable<> zurückgegriffen habe, ist lediglich meiner Faulheit geschuldet ;-) (Mit IEnumerable<> kriegen ist nämlich die Verkettung geschenkt, weil der Typ selbst schon eine Monade ist.) Ebenso die Methode Continue() der Option-Klasse. Hätte ich mir mehr Mühe gegeben, hätte ich statt IEnumerable<> eine spezielle Monadenklasse gebastelt. Das wäre sauberer gewesen.

Für den Punkt, den ich hier rüberbringen wollte, geht es aber auch so, würd ich sagen. Ich möchte einfach einen weiteren Weg aufzeigen, ohne Null leben zu können – und dadurch lesbaren Code zu bekommen. Die Verkettung könnte noch weiter gehen, falls mehr “Transformationsschritte” nötig wären, die immer wieder Output erzeugen, der Input für den nächsten sind. Die Leistung der Monade besteht darin, die Schritte abzubrechen, falls kein Ergebnis geliefert wird (der frühere Null-Fall). Die verbirgt die Logik der Prüfung, ob ein Zwischenergebnis vorliegt und es noch weitergehen soll.

Die C#-Mittel bleiben dabei immer etwas hinter denen von F# zurück. Doch mir scheint “das Denken in Option-Werten” durchaus den einen oder anderen Versuch wert. Wie gesagt, mit etwas mehr einmalig aufgewandter Zeit, ist das auch noch eleganter ;-)

Sonntag, 2. Mai 2010

Es hilft nichts, dass es darauf ankommt – nicht nur beim Null-Problemo [OOP 2010]

Erst ein Feiertag, jetzt ein Sonntag und die Diskussion um “Null oder nicht Null” geht weiter. Als hätten diese Softwareentwickler nichts anderes zu tun… ;-) Ilker hat versucht, einen ausbalancierenden Schlussstrich unter die Diskussion zu ziehen, um uns wenigstens ein Rest schönes Wetter genießen lassen zu können. Doch mich wurm da noch etwas. Und weil der Stein, an dem ich mich da anstoße, bei ihm so ausdrücklich in seinem Blogartikel zu lesen ist, greife ich auch am Sonntag Morgen nochmal in die Tasten:

Woran ich mich in dieser Diskussion – die allerdings nur stellvertretend für viele andere ist – störe, das sind Aussagen wie

“Es kommt auf den Anwendungsfall an. Die beliebte ‘It-Depends’-Weisheit streckt das Thema mit geballter Kraft nieder.”

und

“So oder so – beides ist machbar und beides hat seine Berechtigung.”

Ich kann das einfach nicht mehr hören. (Sorry, Ilker, ist nix Persönliches. Diese Formulierungen sind vielmehr ein Anti-Pattern unserer Branche.) Kommt irgendeiner mit ‘ner Frage, was er tun soll – diese oder jene Technologie oder dieses oder jenes Sprachfeature oder diesen oder jenen Lösungs weg? –, dann antwortet die Branche unisono: “It depends!”, “ Kommt darauf an… “, “Im Prinzip ist alles möglich.”

Was soll denn das? Ja, ja, ich mache so einen Spruch auch mal. Am Ende sind diese Aussagen aber doch ein Armutszeugnis. Vor allem aber: Sie helfen nicht. Niemandem. Vor allem nicht dem, der gefragt hat.

“Es kommt darauf an…” ist eine triviale Antwort. Sie ist trivial und nutzlos, weil sie quasi immer wahr ist. Es kommt immer im Leben darauf an. Auf irgendwas. Wo Wahlmöglichkeiten bestehen, da kommt es immer auf den einen oder anderen Faktor an, ob die eine oder andere Möglichkeit gewählt werden sollte. Diese Faktoren mögen subjektiv sein, also rein persönlich, oder objektiv, d.h. irgendwie aus der Problenstellung bzw. der angestrebten Lösung abgeleitet.

Also: Leute, ihr habt/wir haben immer Recht, wenn wir sagen “Es kommt darauf an…”. Super. Lasst uns darauf einen trinken. Und dann lasst uns endlich damit aufhören. Wer “Es kommt darauf an…” sagt (oder etwas Sinngleiches), der zahlt in Zukunft 5 EUR in das Phrasenschwein. Wie wäre das? Wir kommen einfach nicht voran mit solchen Sprüchen. Sie dienen nie-man-dem. Wir bringen weder den einzelnen Frager noch “unsere Kunst” damit voran.

Ok, so ein Spruch geht also nicht mehr. Was aber dann? Wir sollten, wann immer wie reflexartig “Es kommt darauf an…” sagen wollen, innehalten. Mund zu, Denkmaschine anwerfen… Das halte ich für den ersten Schritt. Nachdenken, ob denn aus einer Problembeschreibung sich nicht doch irgendwie ein Hinweis auf eine vorzuziehende Lösungsmöglichkeit ergibt.

Ja, Nachdenken verbraucht auch Kalorien, aber wir sollten uns diesen Sport gönnen. Der bringt uns selbst und “unsere Kunst” nämlich voran. Die Ergebnisse sind nämlich immer positiv: Schärfung der Wahrnehmung, Reflexion des eigenen Tuns, Ausarbeitung von Regeln.

Eine Regel hat sich ja auch in dieser Diskussion um das Null-Problemo herauskristallisiert:

Null-Regel #1: Man darf Null benutzen und auch als Wert zurückgeben – aber Vorsicht! Erst prüfen, ob es nicht auch anders geht.

Das ist doch schon mal ne schöne Sache, oder? Darin sind wir uns einig. Prost!

Nur verstehe ich nicht, warum Ilker dann schreibt

“Für mich gibt es beim Umgang und bei der Anwendung von Null keine Faustregel.”

Denn diese Regel ist doch eine Faustregel. Sie lautet: Setze Null eher nicht ein. Punkt. Warum sollten wir das runter spielen.

image Mein Gefühl ist, dass “die Branche” da an einem kollektiven mangelnden Selbstvertrauen leidet. “Man” traut sich nicht, einen Pflock in den Boden zu hauen. Denn wer Pflöcke in den Boden haut und die dann auch  noch mit Latten zu einem Areal verbindet, der läuft Gefahr, dass andere einen Streit von diesem Zaun brechen. Und das will “man” doch nicht. Ne, Streit ist doof.

“Wer Regeln formuliert oder verbreitet, wird mit Stirnrunzeln oder gar flame comments bestraft.” – so lautet das unausgesprochene Gesetz.

Regeln stören den Seelenfrieden dessen, dem sie “aufgedrückt” werden. Oder sie stören womöglich sogar den Seelenfrieden dessen, der sie aufstellt. Denn wer Regeln formuliert, der muss sich ja auch daran halten. Will “man” das aber? Wer weiß, vielleicht findet “man” es morgen bequemer, es anders zu tun – und dann wäre die Regel hinfällig. Aaargh… Also besser mal keine Regel aufstellen. Und schon gar nicht anderen die Befolgung einer Regel nahelegen. Denn was auch, wenn die damit nicht zum Erfolg kommen? Dann sind sie sauer auf “einen”.

Das sind natürlich sehr verständliche Regungen. Aber: Leute, Leute, so kommen wir nicht voran. Niemand behauptet, dass “alles” reglementiert werden sollte oder gar könnte. Angst, in der “künstlerischen Programmiererfreiheit” eingeschränkt zu werden, ist völlig (!) unbegründet. Maler, Bildhauer, Schauspieler, Musiker… alle sind vielmehr erst Künstler durch die Regeln ihres Metiers. Ohne Regeln keine Kunst. Denn Kunst ist sozusagen “mit und in den Regeln schaffen und spielen”. Ohne Regeln ist alles keine Kunst. Wir schätzen einen Meister seines Metiers dafür, dass er “gar künstlich” (d.h. soviel wir “nach den Regeln der Kunst” oder “virtuos”) etwas (er)schafft.

image Wer am leeren Tisch sitzt und hört, “Schaffe mal ein Kunstwerk”, der hat es schwer, nicht leicht. Sitzt er aber vor einer Staffelei mit Ölfarbenpallette und Pinsel in der Hand und bekommt den Auftrag “Schaffe ein impressionistisches Landschaftsbild”, der kann loslegen. Öl, Pinsel, Leinwand, Stilrichtung, Motiv: das sind Begrenzungen mit impliziten Regeln, die erst die Möglichkeit bieten, Künstler zu sein.

(Ja, ja, es gibt immer mal wieder auch Künstler, die dadurch groß werden, dass sie (scheinbar) regellos oder gegen die Regeln arbeiten. Aber Achtung: Erstens können sie sich nur profilieren im Kontrast zu etablierten Regeln der Kunst, zweitens schaffen diese Künstler durch ihr Tun (bewusst oder unbewusst) wieder neue Regeln, und drittens sind solche Regelwerk sprengenden bzw. ausweitenden Künstler die Ausnahme. Wer mein, ohne Regeln auszukommen, läuft also Gefahr, sich zu überschätzen.)

Bottom line: Ohne Regeln geht es nicht. Und jeder, der in einer Situation sitzt, wo er nicht recht weiter weiß, der sehnt sich nach einer Regel, die ihn leitet. Warum also diese branchenweite Ambivalenz? Einerseits Regeln suchen für die eigene Anwendung, andererseits aber keine Regeln formulieren wollen, wenn “man” gefragt wird.

imageOhne Regeln ist das Verhalten ein Rumgeeiere wie hier von Ilker trefflich beschrieben:

“Ich gebe in einigen Methoden NULL zurück. Meistens genau dann, wenn ich wirklich damit ausdrücken möchte, dass etwas katastrophales passiert ist. Ich finde es sehr schwerwiegend, NULL als Zuweisung oder Rückgabe stehen zu lassen – aber ich mache es manchmal ganz bewusst.”

Ja, was denn nun? Keine Faustregel, aber “manchmal ganz bewusst”? Wie geht das? Bewusstsein erfordert (!) einen Raum, in dem es sich eben bewusst verortet durch Entscheidungen. Und die Wände dieses Raumes bestehen (zumindest zum Teil) aus… Regeln. Die mögen sehr speziell sein, aber immer noch sind es Regeln, die man dann auch äußern kann, um sie zur Diskussion zu stellen.

Thomas hatte das in seinem ursprünglichen Blogartikel getan:

“Außerdem erwarte ich von jeder Methode null als Rückgabewert für den Fall, dass keine Daten in der Datenbank gefunden wurden.”

Jede Methode (seines Repositories) liefert bei Erfolg ein Objekt und bei Misserfolg Null. So war seine Regeln. Ob die gut oder schlecht ist, sei mal dahin gestellt. Das wurde ja auch ausführlich diskutiert. Zumindest war das eine Regel basierend auf einer bewussten Entscheidung. Die hat er geäußert. Nur so konnte darüber ein Diskurs beginnen. Wunderbar.

Ilker hingegen sagt, er sei bewusst – eine Regel (außer der obigen Null-Regeln #1) mag er aber eben nicht benennen. Das finde ich sehr, sehr schade. Denn so – ich wiederhole mich – kommen wir eben nicht voran.

Oder? Hoppla, jetzt hab ich mich doch durch Ilkers Statement, er hätte keine (Faust)Regel verwirren lassen. Natürlich hat er welche. Die Regel #1 und auch noch seine persönliche Regel:

Null-Regel #2 (von Ilker; noch zu diskutieren): In Katastophenfällen darf Null zurükgegeben werden.

Das ist doch mal eine Aussage. Darüber kann man diskutieren. Aber nicht hier.

@Ilker: Vielleicht magst du ja in einem weiteren Blogartikel ausführen, was Katastrophenfälle sind und warum dann Null als Rückgabewert erlaubt ist und wie zwischen Null und Exceptions als Mittel zur Anzeige von Katastophenfällen entschieden werden sollte. Darüber würde ich gern mehr hören.

Ilker hat also Regeln (mindestens zwei), zieht sich aber darauf zurück, dass “die beliebte ‘It-Depends’ Weisheit” im Falle Thomas “alles niederstreckt”. Warum? Warum keine klare Aussage, keine Empfehlung mit Verweis auf eine seiner Regeln? Weiß Ilker nicht mehr weiter? Das glaube ich nicht.

Ich möchte ihm stellvertretend für uns alle zurufen: Nicht verzagen! Mach doch eine klare Aussage. Habe eine Präferenz. Zeige Kontur. Mach dich sichtbar – ja, und auch angreifbar. Haue einen Pflock in den Boden.

Denn niemand wird dich oder irgendwen für soetwas verhaften. Entweder ist die Empfehlung für den konkreten oder auch allgemeinen Fall richtig/praktikabel – oder es stellt sich im Diskurs heraus, dass sie es nicht ist. Dadurch können doch dann aber alle nur gewinnen. Und niemand steht deshalb schlechter da. Auch nicht der, der die Regeln/Empfehlung ausgesprochen hat.

Niemand muss ja auch seine Regel/Empfehlung kampflos ausgeben. Nur weil ein anderer damit nicht übereinstimmt, heißt das nicht, das man falsch liegt. Solange wir einen Diskurs führen, also sachlich bleiben, solange kommen wir voran – und können am Ende auch mal unterschiedliche Meinungen behalten. In der Malerei gibt es Schulen, in der Programmierung kann es die auch geben. Da könnte es z.B. die “Null geht gar nicht, nie, nimmer!”-Schule geben oder die “Null ist überhaupt kein Problem”-Schule oder die “Null mit Vorsicht”-Schule oder die “Null nur mit Vorsicht und mit folgenden Regeln”-Schule.

image Ich gehöre – soviel kann ich sagen – zur letzteren. Deshalb hier mein Pflock im Boden:

Null-Regel #3 (Ralfs Vorschlag): Null darf nur Rückgabewert sein, wenn Null zur selben Kategorie gehört wie ein Nicht-Null Rückgabewert.

Darüber können wir gern diskutieren. Einen Anfang habe ich hier gemacht. Also: Klare Aussage von mir, kein Rumgeeiere. Und jetzt die Argumente dagegen. Aus der Community. (Zur Regel #2 sage ich etwas, wenn Ilker sie begründet. Hier nur soviel vorab: Ich bin dagegen ;-)

Und jetzt noch ein letzter Schwenk zurück zum Allgemeinen. Wir brauchen also Regeln. Wir sollten uns der Reflexäußerung “Es kommt darauf an…” enthalten. (Auch das ist übrigens eine Regel ;-) Noch ein Pflock von mir im Boden.) Besser, wir schalten in solchen Situationen erstmal die Denkmaschine an. Analyse hilft.

Ebenfalls hilfreich ist allerdings auch: Fragen und Zuhören! Denn so mancher, der sagt “It depends”, leidet womöglich nicht unter mangelndem Selbstvertrauen oder Denkfaulheit, sondern schlicht unter Unwissen. Er weiß nicht genug, um eine Entscheidung zwischen den Alternativen klar treffen zu können.

Dem kann dann durch Nachfragen und genaues Hinhören abgeholfen werden. Wenn man sich denn als Befragter überhaupt interessiert. Womit wir bei einem weiteren Grund für “Es kommt darauf an…”-Antworten sind: Desinteresse. “Was interessiert mich das Problem des anderen? Ich hab schon selber genug Probleme. Wenn mir doch mal einer helfen würde…” usw.

Hieraus ergibt sich auch eine Empfehlung für Hilfesuchende: Wer fragt und “It depends” als Antwort bekommt, der sollte nicht zufrieden sein. “It depends” ist trivial und beliebig. Wer fragt, sollte die Antwort als Aufforderung verstehen, mehr über das Problem zu erzählen oder überhaupt mal festzustellen, ob der Befragte überhaupt ein Interesse an einer Antwort hat. (Von einer Kompetenz zur Antwort sehe ich hier mal ab. Jeder ist kompetent zumindest insofern, als dass er dem Fragenden ein Spiegel sein kann. Auch das hilft oft.)

Fazit: Es hilft also nichts, dass wir die Hände in die Luft werfen und sagen “Es kommt darauf an…” Diese Phrase können wir uns in Diskussionen sparen. Sie macht nur Sinn als Replik auf eine ebenso sinnlose allgemeine Frage. Beispielfrage: “Sollte man die Daten mit einem RDBMS oder einem ODBMS speichern?”, Antwort: “Es kommt darauf an…”

In solche einem Fall mag die allgemeine Frage allgemein mit einer Phrase beantwortet werden, um dadurch aufzuzeigen, dass schon die Frage schlecht formuliert war. In dem Fall ist “Es kommt darauf an…” aber sozusagen ein Stilmittel – dass sofort als solches aufgedeckt werden sollte: “Es kommt darauf an… Das sage ich, weil Ihre Frage zu allgemein ist. Erklären Sie mir bitte genauer, worum es geht.”

“Es kommt darauf an…” ist quasi immer richtig und hilft deshalb nicht. Wir müssen weg von dieser Phrase hin zu mehr Nachdenken und mehr Zuhören. Und daraus müssen sich mehr konkrete Regeln ergeben, die auf konkrete Fälle angewandt werden können. Ob das wenige oder viele, einfache oder komplizierte Regeln sind, ist erstmal egal. Wir brauchen sie als Fundament für “unsere Kunst”.

Niemand behauptet auch, dass die so bewusst erarbeiteten Regeln starr sein sollten. Im Gegenteil. Sie sollen lebendig bleiben im Rahmen eines Diskurses. Zwischendurch mal Ruhe ist auch gut. Einfach mal die Regeln lernen und anwenden, bevor man sie hinterfragt. Aber dann auch gern immer wieder Infragestellung. Die Zeiten ändern sich, also dürfen sich auch die Regeln ändern. Manche langsamer, manche schneller.

Unzweifelhaft ist jedoch, dass wir sie brauchen. Ich glaube sogar, dass wir als Branche unterspezifiziert sind mit Regeln. Deshalb habe ich auch www.clean-code-developer.de (CCD) mit gegründet. Und der Erfolg der Initiative gibt meiner Vermutung recht, würde ich sagen. Denn CCD ist eine Sammlung von Regeln und darauf stehen offensichtlich mehrere Tausend Entwickler, wenn ich mal die Mitgliedszahlen in den Foren und die vergebenen Armbänder zusammenrechne.

So, nach diesem Ausflug ins Grundsätzliche gern wieder zurück zum Konkreten: Da es beim Null-Problemo genauso “darauf ankommt” wie sonst im Leben, stellt sich immer noch die Frage: Worauf kommt es denn an? Was sind die Regeln für die Null-Verwendung? Was sind damit die Eigenschaften eines Szenarios, die geklärt werden müssen, um zu entscheiden, welche Regel zum Einsatz kommt?