Follow my new blog

Samstag, 1. Oktober 2011

Ungesunde Zahlungshemmung

imageZahlen für Fortbildung? Gerade habe ich eine Folge von Uncle Bobs Videos gekauft. Dabei habe ich ein Zucken gespürt. "12 USD für ein Video? Lohnt sich das?" Trotz meiner Zweifel habe ich gezahlt...

...und als bei Paypal dann stand "8,96 EUR", habe ich gedacht: "Wie blöd bin ich eigentlich? Ich denke darüber nach, 8,96 EUR für meine Fortbildung auszugeben - aber ich zucke nicht, wenn ich für den selben Preis einen Roman kaufe oder ins Kino gehe. Das ist unverhältnismäßig."

Ja, genau, das ist unverhältnismäßig. Mein Zucken war Symptom eines verqueren Wertesystems: Entertainment darf Geld kosten, Weiterbildung darf kein Geld kosten.

Das, was in Zukunft Geld bringt (weil es meine Kompetenz erhält oder vergrößert), darf kein Geld kosten - das, was mich Entspannt (auch wichtig, klar), also kein Geld bringt, darf Geld kosten?

Natürlich ist das Missverhältnis nicht so krass. Es geht nicht um Geld ausgeben dort und gar kein Geld ausgeben hier. Ich gebe selbstverständlich auch sonst Geld aus für Weiterbildung (insb. in Form von Büchern) und manches Entertainment ist mir zu teuer.

Mein Gefühl ist jedoch, dass das Zucken auf ein grundsätzliches und nicht nur bei mir Vorhandenes Missverhältnis hinweist. Wir haben durch das Internet als bodenlosem Becher der kostenlosen Information schleichend eine ungesunde Attitüde entwickelt. Ja, ungesund ist sie, so glaube ich, weil sie letztlich unsere ökonomische Gesundheit untergräbt.

Die Sache mit dem Geld ist im real existierenden Wirtschaftssystem essenziell. Wir brauchen es für alles mögliche. Deshalb sind wir darauf angewiesen, dass wir Geld verdienen. (Das mag man unschön finden, ist aber erstmal so.) Also verlangen wir für unsere eigene Arbeit gutes Geld. Wir finden, wir verdienen den Preis, den wir aufrufen (in Form von Stundensatz oder Gehalt).

Wir wollen also viel Geld bekommen - anderen wollen wir aber möglichst wenig Geld geben. Wir wollen die Leistung von Uncle Bob möglichst umsonst (zumindest war ich einen Moment enttäuscht darüber, dass seine Videos nicht umsonst wie sein Blog sind (oder nicht viel weniger kosten)), wir wollen die Leistung von MSDN online kostenlos, wir wollen die Leistungen der vielen Blogger kostenlos, wir wollen ein kostenloses Stackoverflow, wir wollen ein kostenloses Coderepository online usw. usf.

Wir sind maßlos in dem, was wir alles kostenlos haben wollen.
Gleichermaßen sind wir aber maßlos in dem Geld, was wir eigentlich für unsere eigene brilliante Leistung bekommen wollen.

Das ist doch eine Asymmetrie, ein Missverhältnis, das nicht funktionieren kann. Das kann nur zu Spannungen führen. Alles bekommen, aber nichts zahlen? So "denken" 3jährige. Als Erwachsene sollten wir es besser wissen.

Die Preisspirale zu unserem Nutzen umkehren

Wir müssen uns einfach immer wieder klar machen: Wenn wir selbst so denken, dann denken auch unsere Kunden so. Die fragen sich auch, warum sie uns eigentlich Geld geben sollten. Warum sollten sie für unsere Software etwas bezahlen, wenn es doch woanders Open Source kostenlos gibt?

Wie fühlt sich das für Sie an? Ihr Kunde mag es Ihnen (bzw. dem Vertrieb Ihres Unternehmens) nicht sagen, aber denken wird er es: “Boah, soviel Geld… warum soll ich das ausgeben? Im Internet gibt es soviel Software kostenlos.”

Ist das ein gutes Gefühl, wenn Sie sich überlegen, wie Ihre Kunden über Sie denken? Unabhängig davon und erstmal wichtiger: Wohin führt denn eine solche Haltung? Sie setzt eine Abwärtsspirale in Gang. Und die dreht sich ja auch schon. Uns wird schon schwindelig davon, wenn wir auf weithin sinkende Löhne und Stundensätze blicken.

Dann reagieren wir reflexartig: Kosten senken! Am besten selbst alles billiger fordern. Warum kostet Uncle Bobs Video eigentlich soviel wie ein normaler Kinobesuch? Kann der das nicht billiger machen für mich als armen gebeutelten Entwickler, der unter Kostendruck steht und dem die Kunden nicht mehr soviel Zahlen wollen?

Merken Sie es? Alles dreht sich. Nach unten. Weil Sie und ich so “kleinlich denken”. Wir glauben, wir könnten (langfristig) einen Blumentopf gewinnen, indem wir immer billiger einkaufen – aber an unseren eigenen Umsatzforderungen nichts ändern.

Das ist Quatsch.

Entweder sind wir konsequent und senken auch unsere Forderungen.

Oder wir sind konsequent und erhöhen unsere Ausgabenbereitschaft.

Der Vergleich des Preises von Uncle Bobs Video mit einer Kinokarte hat mir (mal wieder) gezeigt, wie meine Entscheidung ausfallen sollte: Ich erhöhe meine Ausgabenbereitschaft. Das ist mein klitzekleiner Beitrag zur Umkehr der Spiralbewegung. Denn wenn ich bereit bin, Geld auszugeben – insbesondere für etwas, das mir ja hilft, meinen Wert zu halten/steigern –, werden vielleicht auch andere Menschen so denken. Das kommt dann auch mir zugute.

Wertschätzung

Der rohe Geldfluss ist aber nur eine Hälfte des Problem bei diesem Missverhältnis. Geld ist nicht alles. Wir wollen auch für unsere Arbeit geschätzt werden. Wir sind ja (hoffentlich) stolz auf das, was wir leisten. Allemal die Software Craftsmen sollten sich diesem Gefühl verbunden sehen.

Wie drücken wir denn aber anderen unsere Wertschätzung aus? “Gut gemacht!” mag ja im Kollegenkreis probat sein, doch in anonymen Beziehungen ist es schwieriger. Klar, der positive Kommentar eines Leser zu einem Blogbeitrag von mir freut mich. Und weitere positive Kommentare freuen mich noch mehr.

Positive Kommentare skalieren jedoch nur schlecht. Und irgendwann, ja, das gebe ich zu, irgendwann sind sie mir auch nicht mehr genug. Denn von positiven Kommentaren kann ich meine Miete nicht bezahlen. Niemand lebt von Luft und Liebe und Schulterklopfen. So ist das halt grad noch in unserer Welt. Deshalb haben wir gelernt, Wertschätzung in Geld auszudrücken und Geld als Wertschätzung zu akzeptieren. (Und wer das nicht glaubt, der verzichte einfach auf die nächste Gehaltserhöhung.)

Dass nicht-monetäre Wertschätzung ihre Grenzen hat, zeigt sich für mich auch im real existiert habenden Sozialismus. Dort wurden Orden als Wertschätzung verteilt. Helden der Arbeit wurden gekürt. Aber hat das zufrieden gemacht? Offensichtlich nicht dauerhaft.

Wertschätzung in einer Form, die den Empfangenden in die Lage versetzt, sie auch noch in die Erfüllung anderer Bedürfnisse zu konvertieren, ist doppelt gegebene Wertschätzung.

Geld ist nicht alles. Geschenke zu Weihnachten sollten nicht Bedeutung allein durch ihren Preis bekommen. Das ist selbstverständlich. Nur geht es mir grad nicht um Weihnachten, sondern eine essenziell ökonomische Situation: die Nutzung von mit Mühe hergestellten Produkten, seien das Entwicklervideos oder Code-Repositories.

Wenn wir für diese Produkte Geld bezahlen, dann tragen wir nicht nur zu ihrem Erhalt bei (s.o.), sondern drücken damit auch noch Wertschätzung aus. Das sollten wir nicht abtun.

Wir möchten wertgeschätzt werden? Dann sollten wir bewusster selbst wertschätzen. Das kann in Form von positiven Äußerungen sein – am Ende skaliert Geld aber besser. Uncle Bob könnte die vielen wertschätzenden persönlichen Kommentare gar nicht wertschätzen. Aber wenn er auf seine Paypal-Abrechnung schaut, fühlt er sich bestimmt wertgeschätzt. Das mag dann abstrakter sein, aber es kommt an. Ja, ich glaube, dass Uncle Bob meine 12 USD Wertschätzung wahrnimmt. Nicht persönlich, aber doch.

Wertschätzung dreht sich auch in einer Spirale. Sie wird allseits größer, wenn wir bereit sind, wertzuschätzen. Sie wird kleiner, wenn wir damit zurückhaltend sind. Was wollen wir?

Beim Geld wie bei der Wertschätzung ist es so, dass wir (mittel- bis langfristig) nicht damit rechnen können, etwas zu bekommen, was wir nicht bereit sind zu geben.

Das Zucken anlässlich Uncle Bobs Video hat mich aufgerüttelt. Ich werde mich bemühen, in Zukunft bewusster erstmal zu Geben.

Dienstag, 13. September 2011

Spinning – Vorschlag für den Kern jedes Vorgehensmodells

Über eine Iterationslänge von 1 Tag habe ich ja schon öfter geschrieben. Dazu stehe ich weiterhin. Allerdings ist mir aufgefallen, dass sie nicht an erster Stelle stehen sollte. Deshalb versuche ich mal eine andere Formulierung:

Im Kern von Scrum steht der Sprint von mehreren Wochen, d.h. ein fixes Auslieferungsdatum mit fixem Scope. Scrum ist damit fundamental zeitorientiert. Pro Sprint werden mehrere "Arbeitspakete" angegangen, die alle zum selben Zeitpunkt abgeliefert werden müssen.

Im Kern von Kanban steht die Warteschlange. Kanban ist damit fundamental flussorientiert. Wie lange die Ablieferung eines "Arbeitspaketes" dauert, ist egal, solange dadurch keine Verschwendung entsteht (Halden von auf Vorrat produziertem "Zeug").
Klingt irgendwie alles sinnig, oder?

Wenn wir uns das aber mal auf der Zunge zergehen lassen, dann sollte uns "im Abgang" ein bitterer Geschmack auffallen. Es fehlt nämlich etwas. Und was fehlt, ist oft schwer zu erkennen.

Es fehlt der Kundennutzen, es fehlt die Qualität. Beides steht weder bei Scrum noch bei Kanban an erster Stelle. Und nur das meine ich natürlich: Er steht nicht an erster Stelle. Er ist nicht offensichtlich.

Ich will also nicht sagen, dass Scrum und Kanban nicht an Kundennutzen oder Qualität interessiert seien. Nein, im Gegenteil. Teams, die Scrum oder Kanban oder Scrumban machen, wollen selbstverständlich Qualität herstellen. Niemandem soll der Wille zu Qualität abgesprochen werden.

Allerdings glaube ich, dass ein Qualitätsziel schwieriger als nötig (oder wünschenswert) erreicht wird, wenn es nicht an erster Stelle steht. Außerdem haben wir es sowieso schwer, ein Qualitätsziel zu erreichen; wie messen wir Qualität? Und je größer der Brocken, der in Qualität hergestellt werden soll, desto schwieriger.

Viel leichter ist es, einem Prozess zu folgen. "Haben wir X getan, so wie es der Prozess vorschreibt?" Diese Frage ist ungleich einfacher zu beantworten.

Je weiter oben auf der Prioritätenliste eines Vorgehens dann eine Handlung steht, desto eher wird sie auch ausgeführt. "Haben wir den Sprint eingehalten?", "Ist die Warteschlange übergelaufen?" - das sind die beiden ersten Fragen, die sich Scrum- bzw. Kanban-Teams stellen. In beiden kommt Qualität nicht vor.

Wie kann nun ein Vorgehen aussehen, dass Qualität an die erste Stelle setzt? Die Handlung mit höchster Priorität muss mit Qualität zu tun haben. Weitere Handlungen/Aspekte können sich dann auf Zeit oder Fluss oder sonst etwas beziehen. So wie sich weitere Handlungen/Aspekte bei Scrum und Kanban auf Qualität beziehen.

Mein Vorschlag:

1. Füge der Software den kleinsten vertretbaren Nutzen auf dem vereinbarten Qualitätsniveau hinzu.

Das sollte die zentrale, die erste Aufforderung eines Vorgehensmodells für Software sein.

Nutzen: Irgendetwas, das für den Kunden einen Wert darstellt, das ihm etwas bringt, das er beurteilen kann, um festzustellen, ob die Entwicklung auf dem richtigen Weg ist. Nutzen kann in Funktionalität bestehen oder der Erfüllung einer äußeren nicht-funktionalen Anforderungen.
Hinzufügen: Die Software soll etwas mehr Nutzen irgendeiner Art erhalten. Der Nutzenzuwachs kann in etwas mehr Funktionalität bestehen oder in etwas höherer Geschwindigkeit oder in etwas besserer Usability usw.

Kleinstmöglich: Es geht nicht darum, eine ganze User Story oder auch nur ein ganzes Feature hinzuzufügen. Nein, es geht um viel kleinere Inkremente, aus heutiger Sicht quasi unvorstellbar und schmerzhaft kleine Inkremente. Phantasie ist gefragt, Anforderungen so hauchdünn zu schneiden.

Damit meine ich natürlich nicht beliebig klein. Wie immer im Leben ist auch hier eine Balance zu finden zwischen Nutzenzuwachs und "administrativem Aufwand". Da aber alle andere Vorgehensmodelle auch nicht ohne Balance und Fingerspitzengefühl auskommen, finde ich es nicht schlimm, wenn es hier auch nötig ist.

Warum so kleine Inkremente? Ich würde ja sogar formulieren, sinnvoll kleinstmögliche Inkremente. Aber dann kommt jemand und sagt, so hauchdünne Scheiben machen ja eben für den Kunden keinen Sinn.

Darum geht es ja aber auch nicht. Selbst die in einem Scrum Sprint realisierten User Stories machen für den Kunden keinen Sinn in dem Sinne, als dass er nach dem ersten Sprint die Software produktiv einsetzen würde. Dazu sind schon ein paar mehr Sprints nötig in den meisten Fällen.

Wirklich sinnvoll ist für den Kunden nur, was ihn im Tagesgeschäft einen guten Schritt voran bringt. Am besten natürlich die Erfüllung der kompletten Anforderungen. Das ist ja aber bei keinem Vorgehensmodell möglich. Scrum liefert das nicht nach einem Sprint und Kanban nicht, wenn am Ende der Produktionskette das erste mal etwas heraustropft.

Der kleinstmögliche Nutzenzuwachs dient also nicht irgendeinem Sprung nach vorn im sofortigen produktiven Einsatz, sondern in einem _beurteilbaren_ Zuwachs. Ein Team kann viel Code in 14 Tagen schreiben - aber ob damit die Software näher an die Wunscherfüllung des Kunden kommt, kann der nicht beurteilen. Agiles Vorgehen stellt mithin den Nutzen in den Vordergrund; es wird in Durchstichen entwickelt. So auch hier.

Nutzen ist alles, dessen Qualität (d.h. Anforderungskonformität) der Kunde beurteilen kann. Nutzen hat also nicht zwingen mit GUI oder Datenbank oder Verteilung zu tun. Deshalb kann Nutzen auch in viel dünneren Scheiben und viel schneller produziert werden, als gemeinhin angenommen.

Und warum nun diesen Schritt als ersten im Vorgehensmodell? Weil er erstens auf äußere und auch innere Qualität fokussiert und zweitens schnelles Feedback ermöglicht und drittens auch noch der Grundbaustein konstanten Flusses ist. “Kleinstmöglicher Nutzen” entspricht der Forderung nach “small batch sizes”.

Qualität und Feedback vor allen anderen Gesichtspunkten. Das ist mir wichtig. Andere kommen erst danach, z.B.

2. Schritt 1 sollte am Ende des Tages abgeschlossen sein.

3. Zurück zu Schritt 1. Es können auch mehrere Nutzeninkremente (nacheinander) gem. Schritt 1 hergestellt werden, solange die Bedingung von 2 erfüllt ist

Ziel ist ein konstanter Fluss von Nutzenzuwächsen. Durch diese Vorhersehbarkeit und Verlässlichkeit entsteht Vertrauen in die sonst so schwer für Außenstehende greifbare Softwareentwicklung.

Das war´s. Das ist für mich der Kern des Vorgehens in der Softwareentwicklung. Und weil sich die dabei so schnell dreht, nenne ich das mal Spinning.

Wer jetzt Zeit oder Fluss weiter ins Spiel bringen möchte, der fühle sich eingeladen:
Kanban passt wunderbar zu Spinning. Nutzenzuwächse sollten nicht auf Halde geplant werden. WIP sollte limitiert werden; am besten arbeitet das Team zur Zeit immer nur an einem Nutzenzuwachs. Wenn die Herstellung des Nutzenzuwachses aus mehreren Schritten besteht, dann sollten die über Warteschlangen entkoppelt werden.

Scrum passt auch zu Spinning. Wenn wirklich, wirklich nötig, können die Nutzenzuwächse mehrerer Tage zusammengefasst dem Kunden zur Beurteilung vorgelegt werden. Der PO kann sie sich am Ende des Sprints anschauen, wenn er mag. Damit würde zwar die Chance der hohen Umdrehungszahl nicht ausgereizt, aber es wäre halt möglich. Ein Team kann Spinning einführen, auch wenn der Kunde noch nicht genauso schnell ist. (Allerdings sollte er dahin gebracht werden.)


Spinning ist also orthogonal zu anderen Vorgehensmodellen. Es kann unabhängig davon eingeführt werden, sogar im Wasserfall. Die Vorteile:
  • Voranschreiten der Codeproduktion im Sinne der Agilität durch höchste Priorität für Nutzenherstellung
  • Fokus auf Qualität durch höchste Priorität für äußere Qualität (Nutzen gem. Anforderungen) und innere Qualität (täglicher Druck auf Evolvierbarkeit durch Nutzenproduktion)
  • Chance für kürzesten Feedbackzyklus (Iterationsdauer max. 1 Tag)
  • Motivation/Zufriedenheit durch “abgeschlossenes Tagwerk”

Wann schicken Sie Ihr Projekt ins Sportstudio? :-) Mit dem Spinning können Sie sofort beginnen.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat...

Dienstag, 6. September 2011

Softwareevolution in Vielfalt

In einem früheren Posting habe ich über Rahmenbedingungen für die Evolution von Software nachgedacht. Den dort genannten möchte ich nun noch eine hinzufügen: die Vielfalt.

Ich glaube, dass Evolution eines Ganzen schwieriger ist, als die Evolution von Vielem, das eine Summe hat.

Das Leben auf der Erde ist nicht ein Organismus (auch wenn es die Gaia-Hypothese gibt), sondern es besteht aus unzähligen. Und es evolviert in jedem einzelnen.

Genauso ist ein Betriebssystem wie Unix oder Windows nicht “ein Ding”, sondern besteht aus Hunderten oder gar Tausenden kleinen “Dingen”, die erst in Summe das ergeben, was wir mit dem Namen “Unix” oder “Windows” betiteln.

Mein iPhone ist einerseits ein “Ding”, eine Hard-/Softwareplattform. Andererseits ist es etwas, das sich entwickelt. Die Summe einer wachsenden Zahl von Teilen, den Apps. Mein iPhone evolviert sozusagen; es passt sich meinen Bedürfnissen ständig an. Das ist ganz einfach möglich, weil es nicht monolithisch ein “Ding” ist wie mein vorheriges Handy. Die Evolvierbarkeit steckt in der Möglichkeit zur Vielfalt.

Ich kann meine Schreib-Anforderungen iterativ immer besser erfüllen, weil ich meine Schreib-Apps getrennt von den Diagramm-Apps und die wiederum getrennt von den Musik-Apps “weiterentwickeln” kann. Ich kann mit “Notizen” beginnen, zu “Nebulous” weitergehen, auf “Pen & Paper” umsatteln und schließlich bei bei “Simplenote” enden.

Die Erfüllung anderer Anforderungen, ist von dieser Weiterentwicklung nicht betroffen. Ich kann auch jederzeit meinen Fokus wechseln. Mal möchte ich beim Schreiben vorankommen, mal möchte ich die Fotonachbearbeitung verbessern.

Diese Offenheit zur Evolution eines Ganzen durch feingranulare Evolution einer Vielfalt von Summanden, halte ich für eine Voraussetzung für evolutionären Erfolg. Unix hat es in Software vorgemacht, der PC hat es in Hardware nachgemacht, das Web hat dieses Prinzip ins Extreme getrieben, das iPhone setzt wieder darauf.

Voraussetzung für eine derartige Evolution der Vielfalt ist eine Plattform. Bei Unix war es der Kernel, würde ich sagen, beim PC der Bus mit seinen Slots für die Erweiterungskarten, beim Web TCP+DNS+HTTP+HTML, beim iPhone Hardware+iOS+AppStore.

Und was bedeutet das für Ihre Anwendung?

Wenn der Kunde kommt und sagt, er wolle “eine Anwendung”, dann nehmen Sie das “eine” nicht so genau. Ihr Kunde will einen Anforderungsberg abgetragen bekommen. Klar. Aber letztlich ist ihm egal, ob das mit 1 Anwendung (lies: EXE) geschieht, solange es praktikabel und nachhaltig geschieht.

Denken Sie also nicht reflexhaft an 1 großes Fenster für die GUI, in dem sich irgendwie alles abspielt. Denn das zwingt den Code in schnell in 1 großen Kasten, in dem er leicht verklebt.

Stattdessen überlegen Sie, wie Sie die Lösung als Summe einer Vielfalt von Apps beschreiben können. Ja, ich meine App in der Linie von iPhone oder iPad Apps. Das sind kleine, sehr fokussierte Programme. Sie dienen einem überschaubaren Zweck. Ihre Usability ist zu dessen Erfüllung maximiert.

Deshalb sehen die Apps auch alle sehr unterschiedlich aus. Eine Foto-App ist ganz anders als ein Spiel und das unterscheidet sich von der Mindmap-App. Das nehmen wir ihnen nicht übel, sondern begrüßen es. Auch, dass wir auf dem iPhone nur jeweils 1 App zur Zeit sehen, stört uns meist nicht. Im Gegenteil: so fokussieren wir uns.

Ich glaube, dass wir diesen Ansatz auf unsere “Geschäftsanwendungen” übertragen sollten. Wir sollten sie zerschlagen in eine Vielfalt von Apps. Und jede dieser Apps kann separat evolvieren.

Beispiel Faktura. Landläufig würde ein Softwarehaus für die Anforderungen, die hinter dem Begriff stehen, eine Anwendung bauen. Eine EXE, die auf allen Desktops im Büro installiert wird. (Und vielleicht noch ein SQL Server auf einem Server-Rechner.) Alle Funktionen würden über ein Hauptfenster erreicht und liefen im Faktura-Prozess ab.

Jede Änderung würde dann notwendig die eine Codebasis betreffen. Unschöne Kopplungen würden bei aller Mühe immer wieder entstehen. Die Evolvierbarkeit würde schnell sinken. (Ja, dagegen kann man sich mit Prinzipien und Praktiken stemmen, doch die grundlegend monolithische Sicht auf die Anforderungen macht es schwer für sie zu greifen.)

Wie anders würde die Entwicklung verlaufen können, wenn das Projekt mit evolvierender Vielfalt angegangen würde. Dann gäbe es eine App für die Rechnungslegung, eine App für die Stammdatenpflege, eine App für den Zahlungseingang, eine App für das Mahnwesen und eine App für den Chef. Vielleicht auch noch eine App für den Import und Export. Oder noch eine App für die Gestaltung unterschiedlicher Rechnungsvorlagen.

Es gäbe nicht mehr eine große Codebasis. Es gäbe ja nicht mehr eine Anwendung, sondern viele, ein Anwendungssystem. Die könnten Codeteile gemeinsam verwenden. Aber sie wären grundsätzlich sehr deutlich getrennt. Änderungen würden dann nicht mehr zu ungewollten Kopplungen in der Codebasis führen, da sie meist nur eine App zur Zeit beträfen. An manchen Apps würde mehr geschraubt als an anderen. Apps, die später in Angriff genommen würden, könnten intern anders aufgebaut sein, als frühere Apps, weil man schon aus Fehlern gelernt hat. Überhaupt müssten Entscheidungen nicht immer für “alles” getroffen werden; keine Notwendigkeit für 1 allumfassende Datenbank, 1 allumfassendes Datenmodell, 1 alle glücklich machende Persistenztechnologie, 1 GUI-Technologie usw.

Eine App könnte ihre Daten in SQL Server speichern, die andere in MongoDb. Die eine App könnte noch mit einer WinForms-Benutzerschnittstelle versehen sein, die andere WPF benutzen. Und wenn in einer App die Unwartbarkeit erreicht sein sollte, dann könnte die ganz unabhängig von allen anderen neu gemacht werden.

Ich kann nur Vorteile in einer solchen Vielfalt erkennen. Sie eröffnet Chancen, ohne einzugrenzen.

Es gibt lediglich eine Hürde: die in unseren Köpfen. Wir müssen Anwendungen so denken wollen. Technisch spricht nichts dagegen, sondern alles dafür. Auch sollten uns die genannten Präzedenzfälle ermutigen. Wir alle lieben Plattformen, die wir stückweise erweitern, die wir evolvieren können. Warum also nicht die Vorteile feingranularer Vielfalt für unsere eigenen Projekte nutzen?

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat...

PS: Einige Leser werden nun denken, ich würde damit vorschlagen, dass eine GUI-Shell oder ein Basisframework wichtig seien. Ohne etwas Gemeinsames unter allen Apps oder über allen Apps sei solche Vielfalt nicht zu machen. Nichts könnte mir jedoch ferner liegen. Anwendungsevolution durch eine Vielfalt an Apps hat keine Voraussetzung; nichts muss erstmal eingerichtet werden. Jede App kann unabhängig von anderen jederzeit begonnen werden. Das Betriebssystem als Plattform reicht.

PPS: Das Ganze nicht als Monolith zu sehen, sondern als Summe von vielen Teilen, sollte sich natürlich durch alle Ebenen einer Software ziehen. Die ganze Anwendung besteht aus unabhängig evolvierbaren Apps. Jede App besteht aus unabhängig evolvierbaren Bausteinen. Und diese Bausteine wiederum sind unabhängig evolvierbar. Apps sind mithin Ausdruck der Selbstähnlichkeit von Software.

Bausteine für die Softwareevolution

Wenn wir Software naturnah entwickeln wollen, also als evolvierbares System, wie sollte sie denn dann aufgebaut sein? In meinem vorherigen Artikel habe ich mir Gedanken über eher organisatorische Voraussetzungen für naturnahe Entwicklung gemacht. Diesmal will ich die technische Seite angehen. Oder besser: wieder angehen, denn vor Jahren schon hatte ich in diese Richtung spekuliert [1]. Damals waren meine Idee eher im Konzeptionellen verblieben - heute habe ich eine Vorstellung von ihrer Implementation.

Voraussetzung Selbstähnlichkeit

Ich glaube immer noch, dass Software nur wirklich gut evolvieren kann, wenn ihre Struktur selbstähnlich ist. Das heißt erstens, dass es in Software mehrere Ebenen gibt. Und zweitens bedeutet es, dass der grundlegende Aufbau aller Ebenen gleich ist.

Ich möchte diese Ebenen jedoch nicht Schicht/Layer nennen. Der Begriff ist überladen und deutet deshalb in die falsche Richtung. Stattdessen wähle ich Stratum; damit lehne ich mich an Abelson/Sussman an, die von Stratified Design sprechen.

Selbstähnlichkeit hat den Vorteil, dass Erfahrungen und Denken nicht ebenenspezifisch sind. Man kann auf einer beliebigen Ebene beginnen und sich sowohl nach oben wie nach unten vorarbeiten, ohne einen konzeptionellen Bruch zu erleiden.

Methoden sind etwas ganz anderes als Klassen, die wiederum etwas anderes sind als Assemblies, die wiederum ganz anders sind als Prozesse. Wenn wir mit diese Mitteln Software in unserem mentalen Modell repräsentieren, dann ist das kompliziert, weil immer wieder anders.

Eine Reduktion auf Klassen allein jedoch ist nicht möglich, da Klassen konzeptionell nichts enthalten außer Methoden. Klassen sind nicht schachtelbar. Sie sind dafür gedacht, Zustand und Funktionalität zusammenzufassen. Das war´s. [2]

Selbstähnlichkeit ist für mich die Voraussetzung, dass Systeme sich entwickeln können. Sie können in der Breite wachsen (innerhalb einer Ebene) oder sie können nach oben wachsen (Abstraktion, Aggregation) oder sie können nach unten wachsen (Verfeinerung, Spezialisierung).

Voraussetzung Unabhängigkeit

Dann glaube ich, dass die Bausteine in den Strata einer Software, unabhängig von einander sein sollten. Abhängigkeiten – statische wie dynamische – sind der Anfang allen Unwartbarkeitsübels. Dependency Injection zum Zwecke der Bereitstellung von Dienstleistungen ist keine Lösung, sondern perpetuiert das fundamentale Problem.

In der Natur finden sich keine Dienstleistungsabhängigkeiten in der Form, wie sie in Software allerorten existieren. Keinem Organismus wird eine Abhängigkeit injiziert. (Nur Parasiten injizieren sich – wir denken an Sacculina, Juwelwespe oder Ridley Scotts Alien.) Organismen brauchen einander, aber nicht in Form von “Referenzen” oder “Handles”. Sie sind vielmehr auf ihre jeweils autonom hergestellten “Outputs” angewiesen. Das ist gelebte Entkopplung.

Dasselbe gilt für den Maschinenbau oder die Elektrotechnik. Einem Motor wird kein Benzintank injiziert, auch wenn er ohne den Treibstoff nicht arbeiten kann. Benzintank und Motor werden lediglich verbunden; es gibt Berührungspunkte, aber keine Abhängigkeiten.

Allemal gibt es keine so breiten Abhängigkeiten wie die durch Interfaces üblicherweise eingegangenen. Ein Motor ist nicht von einem Chassis oder einen kompletten Restauto abhängig. Er hat verschiedene Input-Kanäle, die nur irgendwie gespeist werden müssen. In einem Motorprüfstand wird deutlich, dass das auch ganz anders als mit einem Auto geschehen kann. Dasselbe gilt für einen menschlichen Körper, der ganz ohne Herz und Lunge auskommen kann, wenn an die Gefäße eine Herz-Lungen-Maschine angeschlossen ist.

Abhängigkeiten machen Systeme starr. Evolvierbarkeit setzt daher maximale Unabhängigkeit voraus.

Voraussetzung Nachrichtenorientierung

Die Verbindung zwischen Lebewesen ist immer “nachrichtenorientiert”. Wichtiger ist aber die Nachrichtenorientierung. “Daten” fließen unidirektional von einem Lebewesen zu einem anderen, gezielt oder diffus. Folgt man dem Konstruktivismus, dann ist die Welt (und damit andere Lebewesen) gar nicht direkt erfahrbar. Wir haben lediglich eine begrenzte Wahrnehmungsbreite und empfangen Signale. Die Ausgangspunkte dieser Signale selbst haben wir nie in der Hand. Wir konstruieren sie uns aus den Signalen.

Wenn das für die Evolution der Natur so zentral ist, dann scheint es mir vorteilhaft für Software, die ebenfalls hochgradig evolvieren muss. Ihre Bausteine sollten ebenfalls nur über Nachrichten kommunizieren. Das heißt, es gibt kein Call/Response, sondern nur unidirektional fließende Datenpakete. (Inwiefern die Referenzen auf den Zustand von Bausteinen enthalten können/dürfen, lasse ich hier mal dahingestellt.)

Softwarebausteine verstehen bestimmte Nachrichten, die sie von der Umwelt empfangen. Und sie senden eine bestimmte Menge von Nachrichten an ihre Umwelt. Sendung und Empfang sind in der Natur asynchron. Zwischen Softwarebausteinen würde ich Asynchronizität jedoch nicht zwingend voraussetzen.

Die Form evolvierbarer Software

Wenn ich diese Voraussetzungen zu einer Form für Softwarebausteine zusammenfasse, dann kommt das folgende Bild heraus:

image

Ich könnte diesen Softwarebaustein “Objekt” nennen, da er zustandsbehaftet ist und über Nachrichten kommuniziert. Aber “Objekt” ist so belastet, dass ich zumindest heute auf einen neutralen Begriff ausweichen möchte. Ich nenne ihn deshalb “Holon”. Das passt zur Selbstähnlichkeit; Holons sind “Dinger”, die gleichzeitig Teil und Ganzes sind.

Jedes Holon nimmt die Umgebung durch einen “Kanal” wahr und sendet über einen weiteren “Kanal” Signale an seine Umgebung, die dann andere Holons wahrnehmen können. Wahrnehmungen (Input) verarbeitet das Holon zu Zustand und/oder Signalen (Output).

Natürlich kann jedes Holon nur eine bestimmte Menge von Signalen verarbeiten und erzeugen. Signale bzw. Nachrichten bestehen daher nicht nur aus Daten, sondern haben auch noch einen Typ oder eine Bedeutung. Die Zahl 42 mag einmal die Bedeutung einer Antwort auf die ultimative Frage haben und einmal schlicht das Alter einer Person sein.

Aus Holons lassen sich nun Verarbeitungsstrukturen auf mehreren Ebenen (Strata) bilden:

image

Meine These ist, dass Software viel besser evolvierbar wird, wenn wir sie aus Holons aufgebaut denken. Die scheinen merkwürdig eingeschränkt gegenüber unseren heutigen OOP-Objekten. Ich behaupte aber mal, dass einen eventuelle Einschränkung mehr als kompensiert wird durch das, was wir bekommen. Und das wir nur bekommen, was wir wollen – viel mehr Evolvierbarkeit –, wenn wir uns einschränken.

Wir denken mit Holons nicht mehr über Abhängigkeitsverhaue nach. Wir haben einen ganz regelmäßigen Aufbau von Software von der kleinsten Codeeinheit bis zur größten. Wir können ganz natürlich Stratified Design betreiben. Und wir bekommen Bausteine, die sich von Hause aus für asynchrone/parallel Verarbeitung eignen.

Klar, das bedeutet, wir müssen unsere Denkstrukturen ändern und Gewohntes über Bord werfen. Der in Aussicht gestellte Gewinn scheint mir jedoch groß genug, um es damit zu probieren. Und so schwer ist es ja auch nicht, es auszuprobieren. Hier das universelle Interface für Holons:

interface IHolon {
  void Process(IHolonMessage input, Action<IHolonMessage> output);
}

Im Grunde reicht sogar ein Delegat, um jeden Baustein in jedem Stratum zu beschreiben:

delegate void Holon(IHolonMessage input, Action<IHolonMessage> output);

Und die Nachrichten, die zwischen den Holons fließen, sind auch ganz einfach:

interface IHolonMessage {
    string Type {get;};
    object Data {get;};
}

Kaum zu glauben, aber ich denke, dass mit solch einfachen Mitteln sich grundsätzlich alle Softwarelösungen ausdrücken lassen. [3] Diese einfachen Mittel stellen für mich das Rückgrat evolvierbarer Software dar.

PS: Wenn in der Softwareentwicklung Eleganz wirklich einen hohen Stellenwert hat, dann wage ich mal keck zu hoffen, dass eine Softwarestruktur dieser Art doch einigen Appeal hat. Ich zumindest finde sie in ihrer Einfachheit elegant.

PPS: Ich weiß, die Holons sehen aus wie Funktionseinheiten des Flow-Designs. Natürlich gibt es da auch einen Zusammenhang. An dieser Stelle überlege ich jedoch grundlegender. Deshalb haben Holons auch nur einen Eingang und einen Ausgang.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat...

Fußnoten

[1] s. ältere Artikel in der Rubrik “Software als System” in diesem Blog, insbesondere diesen und diesen Artikel.

[2] Das heißt nicht, Objekte seien nutzlos für evolvierbare Software. Nein, ich denke, wir müssen keine neue Programmiersprache erfinden, sondern die vorhandenen nur besser einsetzen.

[3] Natürlich meine ich damit nicht, dass Software bis hinunter zu Ausdrücken aus Holons aufgebaut werden sollte. Mir geht es um das Herunterbrechen von Funktionalität bis zu einer Granularität, dass man begründet zuversichtlich sein kann, ein Blatt-Holon mit wenigen Zeilen konventionellen Codes umsetzen zu können.

Sonntag, 4. September 2011

Voraussetzungen für sich entwickelnde Softwarestrukturen

Liegt die Zukunft der Softwarearchitektur in Emergent Architecture und Growing Software? Diese Frage wurde in zwei Open Space Gruppen auf der SoCraTes Konferenz 2011 behandelt. Das hat mir gefallen, da ich ja mit meinen Softwarezellen auch schon einmal eine naturnahe Metapher gewählt hatte.

In den Diskussionen wurde ich das Gefühl jedoch nicht los, dass wir bei allem Appeal der Metaphern noch zu wenig darüber nachgedacht hatten, was es denn wirklich heißt, naturnah Software entstehen zu lassen. Wie entwickeln sich natürliche Systeme? Was lässt sich davon auf Softwareentwicklung übertragen? Welche Voraussetzungen sind nötig?

Struktur entspricht gegenwärtiger Umwelt

Ganz zentral für naturnahe Softwareentwicklung ist, dass wir die Rolle der Umwelt richtig verstehen. In der Natur ist alles so wie es ist, weil es so am besten zur Umwelt passt. Das ist der Kerngedanke der Evolutionstheorie, würde ich sagen. Alles an allen Organismen hat einen Zweck, den es erfüllt, so wie es ist. Da ist nichts zuviel, nichts zuwenig.

Vor allem ist nicht so entwickelt wie es ist auf Vorrat. Alles ist nur einer gegenwärtigen Umwelt mit ihren über Generationen erfahrenen Schwankungen angepasst. Kein Lebewesen besitzt eine Eigenschaft nach dem Motto “Aber was wäre, wenn Umweltparameter X in der Zukunft ganz anders aussehen würde?”

Leben ist insofern “lean”, d.h. frei von “Fett”. Es gibt keine “Eigenschaftenpolster”. Selbst die Anpassungsfähigkeit ist begrenzt, obwohl sie die wichtigste Metaeigenschaft ist.

Wenn wir wirklich naturnah Software entwickeln wollen, dann müssen wir konsequent auf den Blick in die Glaskugel verzichten. Erfahrung, die uns verleitet zu sagen, “Aber ich weiß doch, dass Kunden in den meisten Fällen noch Y wollen…”, darf nicht zu Anreicherungen in der Software führen. Das höchste der Gefühle ist, dem Kunden einen solchen Gedanken vorzulegen, um durch seine Antwort die gegenwärtige Umwelt besser zu verstehen. Das oberste Prinzip naturnaher Entwicklung ist mithin YAGNI.

Jede Entscheidung darf sich nur vom gegenwärtigen Kenntnisstand leiten lassen. Dann ist die Entscheidung erstens schnell getroffen und zweitens das Ergebnis so schlank wie möglich.

Evolution ist ohne Ziel

Wenn Evolution sich nur an der gegenwärtigen Umwelt orientiert, dann bedeutet das in verschärfter Formulierung, dass Evolution kein Ziel hat. Und das bedeutet, Evolution kann nicht vorplanen. Es gibt kein Vordenken, sondern nur Reaktion, Anpassung. [1]

Das bedeutet für die Softwareentwicklung ein viel größere als bisher übliche Konzentration auf das Hier und Jetzt. Geradezu buddhistisch muss für naturnahe Entwicklung gelten “live in the now”.

Weder Strukturen noch Schritte sollten über das unmittelbar Sichtbare und Notwendige hinaus ausgelegt sein. Zu YAGNI tritt KISS als Leitprinzip.

Evolvierbarkeit ist ein Ergebnis von Evolution

Als Ergebnis der Evolution sehen wir vor allem die umweltangepassten Eigenschaften von Lebewesen. Wir haben verstanden, dass diese Eigenschaften sich durch Feedback ausprägen. “Survival of the fittest” nennen wir das. Eigenschaften entstehen und müssen sich gegenüber der Umwelt behaupten. Passen sie nicht, sinkt die Wahrscheinlichkeit zur Weitergabe der Eigenschaften an die nächste Generation; unpassende Eigenschaften sterben aus. Das Feedback der Umwelt ist gnadenlos.

Auf die Software haben wir das inzwischen in Form iterativen Vorgehens übertragen. Wir stellen Software mit bestimmten Eigenschaften her, von denen wir meinen, dass sie zur Umwelt (den Anwendern) passen – und suchen dann das Feedback der Umwelt. So nähern nähern wir die Eigenschaften in kleinen Schritten der Umwelt an bzw. co-evolvieren Software mit der Umwelt.

Hinter den sichtbaren Eigenschaften evolvierender Systeme gibt es allerdings noch eine unsichtbare Eigenschaft: die Evolierbarkeit. Wie jede Eigenschaft unterliegt auch sie einem Zwang zur Passgenauigkeit.

Meinem Empfinden nach haben wir diese Eigenschaft als ebenfalls zu entwickeln nicht auf dem Zettel. Wir unterwerfen sie in unseren Projekten keinem Feedback. Wir unterwerfen sie nicht einmal branchenweit einem Entwicklungsdruck (oder allenfalls einem nur vergleichsweise schwachen).

Wenn wir über naturnahe Softwareentwicklung sprechen, ist das eine große Nachlässigkeit, da im Kern der gesuchten Naturnähe ja gerade die Evolvierbarkeit steht.

Wie müsste also das Feedback aussehen, um Evolvierbarkeit zu entwickeln?

Ob unsere Software schon performant, skalierbar, sicher genug ist, stellen wir fest, indem wir sie immer wieder auf die Probe stellen. “Los, zeig uns, wie schnell du bist!”, “Ha! Kommst du auch mit dieser größeren Nutzerlast zurecht?”, “Nimm diese Sql Injection Attacke, du Software, du!”

Aber wie ist es mit der Evolvierbarkeit? Was ist eine Herausforderung für sie?

Ich denke, das sollte auf der Hand liegen: Evolvierbarkeit wird durch unerwartete Anforderungen auf den Grill gelegt. “Pass dich dieser überraschenden Anforderung an, Code!”

Das erste, was Evolution lerne musste war die Unvorhersehbarkeit der nächsten Umweltveränderung. Von der hat sie ihre grundlegenden Bausteine, die Moleküle (DNA, RNA), Organellen und Zellen formen lassen. So existiert Leben am tiefsten Meeresgrund genauso wie im Darm und auf dem Mount Everest.

Für die Softwareentwicklung ist das noch eine Aufgabe, denke ich. Unsere grundlegenden Bausteine sind noch nicht wirklich evolutionsunterstützend. Allemal können wir noch viel zu leicht anti-evolutionäre Strukturen damit herstellen, wie die Massen an Brownfield-Code zeigen.

Das bedeutet, wir müssen mehr Druck auf die Evolvierbarkeit ausüben. Sonst hinkt ihre Entwicklung der anderer Eigenschaften hinterher – und behindert deren Evolution. Wir müssen also geradezu auf die Evolvierbarkeit zuallererst Evolutionsdruck ausüben, weil sie für alles andere fundamental ist. Wie soll das aber geschehen?

Nach meinem Gefühl ist das gar nicht schwer. Wir müssen erstens die Frequenz der Änderungsanforderungen drastisch erhöhen. Und zweitens müssen wir die Vorausschau drastisch begrenzen.

Ganz konkret bedeutet das für mich: Software ist jeden Tag mit überraschenden Änderungswünsche zu konfrontieren. Und jeden Tag ist Software auszuliefern. Nur so entsteht auf ihre Anpassungsfähigkeit soviel Druck, dass die wirklich geschmeidig wird. Das gilt für die allgemeine Form von “Softwarebauteilen” wie für die konkrete Struktur einer Lösung. Ohne diesen Druck werden wir nicht dahin kommen, die Prinzipien und Praktiken für dauerhafte Evolvierbarkeit zu erkennen und zu leben.

Evolution geht aufs Ganze

Wer hätte je einen Magen oder ein Gehirn oder eine Hand getrennt von einem Lebewesen sich entwickeln sehen?

Evolution findet immer am ganzen Organismus statt. Ein Lebewesen ist entweder als Ganzes an seinem Umwelt angepasst oder gar nicht. Das beste Herz nützt nichts, wenn das Nervensystem unangepasst ist.

Auf die Softwareentwicklung übertragen bedeutet das, es gibt keine Infrastrukturentwicklung. Jedes Mal, wenn sich Software der Umwelt aussetzt – also am Ende einer Iteration –, setzt sie sich als Ganzes aus. Sonst ist sie nicht “anschlussfähig” gegenüber ihrer Umwelt. Das muss sie aber sein, sonst bekommt sie kein Feedback. Und Mangel an Feedback bedeutet Stillstand und dann Tod.

Alle Arbeit an Software muss deshalb sagen können, inwiefern sie an die Umwelt anschließt, welches konkrete Bedürfnis der Umwelt sie erfüllt. Ein Team als Ganzes muss zu jeder Zeit auf die Software als Ganzes konzentriert sein. (Ein einzelner Entwickler kann sich im Rahmen dessen natürlich auf ein Detail fokussieren. Er dient damit ja einem Ganzen.)

Naturnahe Entwicklung bedeutet also Entwicklung in Durchstichen. In jeder Iteration ist eine ganze, besser angepasste Software abzuliefern. [2]

Evolution ist Verfeinerung

Schließlich scheint mir ein wesentliches Merkmal von natürlicher Entwicklung die Bewegung vom Groben zum Feinen. Evolution ist Differenzierung, ist Verfeinerung.

Die Entscheidung zwischen bottom-up und top-down Entwicklung können wir zur Ruhe betten. Diese beiden Pole sind unwichtig. Wir können die Herangehensweise wählen, wie wir mögen, wie es uns in Bezug auf eine Problemstellung passt.

Worüber wir aber nicht nachdenken sollten, das ist coarse-to-fine oder simple-to-complex Entwicklung. Software sollte schrittweise “schärfer” oder detaillierter werden.

Auch breadth-first vs. depth-first ist hier zweitrangig. Die Verfeinerung kann in der Breite in geringem Maß stattfinden oder in der Tiefe in hohem Maß. Wie es der Kunde mag. Er bestimmt die Prioritäten.

Nach jeder Iteration kann er sich wünschen, ob Feature A verfeinert werden soll oder lieber Feature X grob angegangen werden soll. Vor dem Hintergrund des täglichen Drucks auf die Evolvierbarkeit bedeutet das, der Kurs der Entwicklung kann sich prinzipiell jeden Tag ändern. Und warum auch nicht? Der Kunde, die Anwender: das ist die Umwelt. Der gilt es sich anzupassen.

Naturnahe Entwicklung findet deshalb in kleinsten Schritten statt. User Story, Use Case, ja selbst Feature sind dafür zu groß. Würden sie pro Iteration komplett umgesetzt, würde zu schnell zu viel Detail entstehen. Das wäre keine Evolution mehr, sondern Setzung.

Wenn es jedoch jeden Tag an quasi beliebiger Stelle in der Software ein Nutzenstückchen weiter geht, dann entsteht kontinuierliche Anpassungsfähigkeit. Dann kann auch nie mehr als die Arbeit eines Tages umsonst gewesen sein. Tägliches Feedback korrigiert ja den Evolutionsweg unmittelbar.

Fazit

Agile Softwareentwicklung ist schon ein Schritt in Richtung naturnahe Softwareentwicklung. Erreicht ist das Ziel mit ihr jedoch noch nicht. Wenn wir naturnahe Softwareentwicklung als Tugend ansehen, dann müssen wir ein paar Schrauben noch mehr anziehen. Evolution ist rigoroser als Agilität.

Fußnoten

[1] Ziellosigkeit mag merkwürdig klingen, da doch anscheinend mindestens der Kunde ein Ziel hat. Er möchte schließlich eine Software für einen bestimmten Zweck. Wie oft ist es aber der Fall, dass am Ende der Entwicklung genau das herauskommt, was am Anfang in den Anforderungen drin gestanden hat? Wie oft sind Anforderungen vollständig? Ich halte es deshalb für besser, die Idee von der Zielhaftigkeit im Sinne einer sichtbaren Zielmarke aufzugeben. Softwareentwicklung ist vielmehr ziellos – hat jedoch ein Ende. Beendet ist sie, wenn der Kunde sagt “Genug!”

[2] Mit “ganze Software” meine ich natürlich nicht, dass vom ersten Tag an eine komplette Buchhaltung oder eine ganze Textverarbeitung ausgeliefert werden soll. Wie sollte das auch gehen? Nein, “ganze Software” bedeutet, dass die Software für den Anwender als etwas sinnhaftes wahrnehmbar ist, zu dem er Feedback geben kann. Für eine Buchhaltungssoftware bedeutet das nicht mal, dass es ein GUI geben muss, sondern am ersten Tag vielleicht nur einen Prüfstand, mit dem der Anwender Feedback zum Datenimport geben kann.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat...

Donnerstag, 25. August 2011

Jenseits von SOLID

In einem hübschen Artikel hat Mark Nijhof ein Refactoring nach SOLID beschrieben. Dem ist nichts hinzuzufügen – wenn man denn bei SOLID stehen bleiben will. Ich sehe SOLID aber nicht als sakrosankt an; für mich darf Code gern noch “cleaner” werden.

Hier zunächst die Code-Ausgangslage:

image

Eine Klasse mit eine Menge Verantwortungen. Mark löst sie im Sinne des Single Responsibility Principle (SRP) wie folgt auf:

image

Dagegen ist nichts einzuwenden. Es entstehen drei “Akteure”, d.h. Funktionseinheiten mit jeweils wiederum potenziell vielen Aufgaben. Hinweis darauf ist die Benennung mit Substantiven. Für den Moment hat Mark zwar Verantwortlichkeiten getrennt – doch das ist “Rückspiegelweisheit”: Bei Vorlage einer Brownfield-Klasse ist ihm die Vermengung von Verantwortlichkeiten in einer Klasse aufgefallen.

Wäre es aber nicht viel besser, bei einer Refaktorisierung Code zu erzeugen, der einer solchen Entwicklung von vornherein Widerstand leistet? Vorbeugen ist besser als Refactoring. Dazu bedarf es jedoch einer Idee von der Ursache der Verantwortungsanhäufung. Woher könnte die rühren?

Ich glaube, das hat nicht nur mit Unaufmerksamkeit der Entwickler zu tun, die zum ursprünglichen Stand von OrderProcessor beigetragen haben. Mitschuldig ist auch die Bennung der Klasse. Sie ist als Akteur ausgelegt, als Substantiv, d.h. als Funktionseinheit, die qua Name suggeriert “Stopfe mich voll mit allem, was mit der Auftragsverarbeitung zu tun hat.”

So machen wir es doch auch, wenn ein Mensch als Akteur vor uns steht. “Ach, kannst du das nicht auch noch machen?” Und schon hat er eine Verantwortlichkeit mehr am Hacken.

Um das Wurzelproblem anzupacken, fände ich es besser, die Namen anders zu wählen. Allemal die der Logik-Funktionseinheit. Sie sollte nicht OrderProcessor heißen, sondern Process_order. Denn nur darum geht es hier. Noch schöner wäre es, wenn dazu dann Send_confirmation_email und Save_order dazu kämen:

image

Lassen Sie Ihr Unwohlsein angesichts der merkwürdigen Methodenbezeichnung “_” kurz beiseite. Spüren Sie stattdessen einmal in sich hinein: Wir groß ist nun die Gefahr, dass Process_order oder eine der beiden anderen Klassen mit weiterer Funktionalität aufgeladen wird, die nichts direkt mit dem Klassennamen zu tun hat? Für mich ist da jetzt eine spürbare Barriere.

Nun aber zu einem noch wichtigeren Aspekt im wahrsten Sinn des Wortes, den Mark bei seinem Separierungsbestreben übersehen hat. Er ist schwer zu sehen, weil wir alle so traditionell OO-konditioniert sind. Wir halten den Code in dieser Hinsicht für normal. “So macht man das halt” denken wir und hat Mark gedacht.

Mit geht es um das, was in Process_order passiert. Ich greife es mal heraus:

image

Wieviele Verantwortlichkeiten hat Process()? Wieviele Gründe für Veränderung gibt es?

  1. Muss die Methode “angefasst werden”, wenn die Bestätigungen per Fax statt der Email versandt werden sollen?
  2. Muss die Methode verändert werden, falls nach der Email-Bestätigung auch noch ein Eintrag in ein Protokoll gemacht werden soll?
  3. Muss die Methode überarbeitet werden, wenn sich die Bedingung für eine Email-Bestätigung verändert?

Die Antwort auf alle drei (!) Fragen ist Ja. Die Methode hat mithin nicht nur eine Verantwortlichkeit. Die Fragen gehören nämlich zu unterschiedlichen Verantwortlichkeitsbereichen:

  1. Implementationsauswahl
  2. Integration
  3. Kontrolle

Die Methode kontrolliert die Integration konkreter Implementationen für Operationen. Sie stellt also nicht nur sicher, dass bestimmte Operationen in einer bestimmten Reihenfolge ablaufen, nein, sie entscheidet auch noch über diese Reihenfolge zur Laufzeit und instanziiert ganz bestimmte Ausprägungen der Operationen.

Zur Marks Ehrenrettung sei gesagt, dass er eine dieser Verantwortlichkeiten auch durch seine SOLID-Brille erkannt hat: die Implementationsauswahl. Sie löst er über Inversion of Control auf. (Dass er später auch noch einen DI Container einsetzt, ist nicht so wichtig.)

Also mache ich meine Code-Version auch IoC-konform:

image

Dabei bleibt es dann bei Mark. Er belässt die Verantwortlichkeiten Integration und (!) Kontrolle in der Methode. Er erkennt sie schlicht nicht. Sie liegen außerhalb seines Wahrnehmungshorizonts.

Dabei ist es so einfach: Wo eine Kontrollstruktur – nomen est omen – wie if oder for im Spiel ist, da werden Operationen nicht nur “hintereinandergeschaltet”, sondern auch darüber entschieden, wann die unterschiedlichen Pfade durchlaufen werden. Also muss man ein Augenmerk darauf haben, dass diese Entscheidung nicht ebenfalls in derselben Methode gefällt wird. Darum geht es beim Prinzip Single Level of Abstraction (SLA) – das allerdings nicht zu SOLID gehört. Schade.

Um konsequent SRP umzusetzen, muss die Bedingung, unter der eine Bestätigung gesendet werden soll, raus aus der Auftragsverarbeitung. Das kann zunächst in einfacher Weise geschehen:

image

Aber fühlt sich das wirklich gut an? Mich schüttelt es, weil nun ganz deutlich wird, dass die Bedingung zwei Verantwortlichkeiten hat. Nicht umsonst musste ich einen Namen wählen, der eine Konjunktion enthält. Nur Is_order_valid() hätte die Speicherung unterschlagen, nur Successfully_saved_order() hätte die Validation unterschlagen.

Das Ziel ist richtig, die Verantwortlichkeiten zu entzerren. Aber das Mittel ist falsch. Besser finde ich es so:

image

Jetzt ist deutlicher und ohne Schachtelung die Sequenz des Ablaufs zu sehen:

  • Gültigkeit des Auftrags prüfen
  • Auftrag speichern
  • Email-Bestätigung senden

Hier könnte ich es gut sein lassen. Doch ich bin seit der Lektüre von “Clean Code” sensibel geworden. Die Verantwortungshäufung lauert überall. Vor allem lädt der obigen Code bei aller Sauberkeit immer noch (oder wieder) dazu ein, in ihm weitere Verantwortungen anzuhäufen. Wie schnell ist die Validitätsbedingung aufgebohrt und sieht dann z.B. so aus:

image

Schon wieder steckt die Kontrollverantwortung mit drin.

Und warum? Weil die Kontrollstruktur if in der Methode geblieben ist. Sie ist zwar relativ harmlos, solange sie nur eines tut, nämlich den Codefluss entsprechend eines Flags mal in dieser und mal in jener Richtung zu leiten. Doch wie ein Stäubchen in der Luft ist sie ein Kristallisationskeim: für Domänenlogik. Eben noch steckte die ausschließlich in IsValid, doch dann hat die sich ausgebreitet, weil grad keine Zeit war, über eine Methode auf Order oder sonst wo nachzudenken, die beide Bedingungsklauseln umfasst. Ja, so schnell kann es gehen. So entsteht Entropie (oder dirty code) in kleinsten Inkrementen.

Hört sich vielleicht nach Erbsenzählerei an. Mag sein. Aber wenn ein Container voller Erbsen zerbricht, kann das ordentlichen Schaden anrichten.

Ich meine also, dass die Verantwortlichkeit Integration solange nicht ordentlich herauspräpariert ist, wie in einer Methode noch Kontrollstrukturen stehen. Aber wie kann die Integration der drei Operation zu einem Ganzen – der Auftragsverarbeitung – erreicht werden, wenn sie einerseits von Bedingungen abhängig ist, andererseits jedoch keine Kontrollstrukturen dafür enthalten darf?

Die verblüffend einfache Antwortet lautet: mit Continuations. (Mit Erweiterungsmethoden ginge es auch. Aber damit würden wir uns auf statische Methoden festlegen.)

Meine Version der Refaktorisierung mit Continuations so aus:

image

Die Methode zur Auftragsverarbeitung ist jetzt ausschließlich für die Integration von Operationen im Sinne eines Verarbeitungsflusses zuständig. Und sie lädt niemanden mehr ein, Logik hineinzustecken. (Naja, wer will, der bohrt natürlich die Lambda-Ausdrücke auf. Aber das halte ich für weniger naheliegend als mal eben eine Bedingung zu erweitern.)

Validate_order() ist ebenfalls auf eine Verantwortlichkeit konzentriert: Kontrolle. Wer die Validitätsbedingung verändern will, ist dort genau richtig. Was anderes kann man dort aber auch nicht sinnvoll tun. Insofern muss der Ausdruck auch nicht in eine weitere Methode ausgelagert werden.

Zum Schluss noch zu den merkwürdig benannten Methoden der zum Speichern und Senden des Auftrags. Ich habe die Namen auf einen Unterstrich beschränkt, um den Code besser lesbar zu halten. Oder ist _save._() nicht besser zu lesen als _save.Process() oder _save.Save()?

Dennoch verstehe ich, wenn Sie bei “_” als Methodenname zucken. Er scheint so nichtssagend. Klar. Er muss ja auch nichts sagen, weil der Klassenname (und der davon abgeleitete Feldname) alles sagt.

Fallen Sie angesichts Ihres Unwohlseins nun aber nicht zurück in alte Gewohnheit. Prügeln Sie sich aber auch nicht, “_” als Methodenname zu akzeptieren. Sondern machen Sie den nächsten konsequenten Schritt, den Mark ebenfalls nicht vollzogen hat. Den Robert C. Martin sich vor ihm nicht getraut hat.

Denken Sie das Interface Segregation Principle (ISP) weiter.

In Marks Artikel kommt das ISP in Bezug auf den Ausgangscode eigentlich gar nicht zum Einsatz. Er muss dafür weitere Einsatzszenarien erfinden. Das liegt daran, dass das ISP eben ein I-SP ist; es beharrt darauf, Dienstleistungen in Bündeln anzubieten und zu nutzen. Das IoC und der DI Container arbeiten auf Interfaces, obwohl im Beispiel von jedem Interface nur 1 Methode gebraucht wird.

Das ist nicht nur hier, sondern sehr oft der Fall. Integrationen bekommen Akteure mit vielen Dienstleistungen hineingereicht, nutzen davon aber nur sehr wenige, oft nur 1 oder 2. Warum also überhaupt Abhängigkeiten auf Interfaces basieren? Der Code könnte ohne auskommen, ohne IoC und DI Container zu vernachlässigen:

image

Voilà! Die Unterstriche sind verschwunden. Interfaces sind nicht mehr nötig. Process_order ist von keiner Implementation abhängig. Process_order.Process() hat weiterhin nur eine Verantwortlichkeit.

Fazit

SOLID ist gut. SOLID+SLA ist besser. Und SOLID+SLA+ISP2TheMax ist noch besser.

SOLID hat uns auf dem Weg zu Evolvierbarkeit voran gebracht. Wir sollten uns nun aber nicht ausruhen. Das Ziel ist nicht erreicht. Deshalb dard es gern noch konsequenter prinzipieller zugehen. Schauen Sie genau hin, hinterfragen Sie das Selbstverständliche und Überkommene. Die Kristallisationskeime für Dreck sind manchmal klitzeklein. Über die Zeit entfalten sie nichtsdestotrotz eine ungute Wirkung. Versuchen Sie deshalb, Code schon beim Schreiben in der Form so zu halten, dass an ihm Dreck nicht anhaften kann.

Continuations statt Kontrollstrukturen und Delegaten statt Interfaces sind ein Anfang.

Eine Technik zur Vermeidung von dreckigem Code von vornherein ist besser als eine Prinzip zum Aufspüren von Dreck im Code im Nachhinein.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat…

Mittwoch, 17. August 2011

Mit dem Teppichmesser gegen Verlagsträgheit

Buchhandel und Verlage sind nicht die Schnellsten, was neue Technologien angeht. Ideen aus meiner Sicht als Leser, was der stationäre Buchhandel tun kann, um nachzuziehen und sich zu erhalten, um für mich attraktiv zu bleiben, habe ich schon geäußert.

Für die Verlage gibt es natürlich auch Ideen. Hier nur eine, wie ein cooles Buch 2.0 aussehen könnte.

Das scheint mir Lesen auf der Höhe der Zeit. Wie krude ist dagegen ein Kindle-Book oder ein Hörbuch.

Bis Verlage solche Produkte herstellen, wird allerdings noch einige Zeit vergehen. Auch nicht alle heutigen Verlage werden das schaffen. Andere werden an ihnen vorbeiziehen und die Gunst der Leserschaft erringen. Die Schnellen fressen die Langsamen.

Einstweilen wäre ich ja schon zufrieden, wenn Verlage ganz selbstverständlich jedes Buch auch als eBook herausbrächten. Für Romane und Fachbücher fährt der Zug in diese Richtung langsam los. Bei Sachbüchern und Schulbüchern scheint mir da aber noch einiges im Argen zu liegen.

Case in point: Das Französich Lehrwerk Découvertes von Klett, mit dem nun meine Tochter in der 6. Klasse beginnt. Der Verlag bietet keine eBook-Version an. (Warum nur? Weil Kinder keinen Computer haben? Das Gegenteil scheint mir der Fall. Alle haben ein Handy, fast alle haben einen Computer zuhaus. Meine Tochter – naja, wie könnte es anders sein ;-) - sogar zwei und noch ein eigenes iPad.)

Ich möchte nun mit meiner Tochter einen erneuten Anlauf nehmen, Französich zu lernen – aber natürlich auf meine Weise. Das heißt ich werde die Vokabeln mithilfe von FlashCards Plus Pro auf iPad und iPhone lernen. Und ich werde das Lehrbuch auf dem iPad und iPhone studieren – ob der Verlag das will oder nicht.

Wie das? Er bietet doch kein eBook an.

Ganz einfach mit roher Gewalt :-)

image

Bauanleitung – Aus einem Papierbuch wird ein eBook

Wenn der Verlag es nicht schafft auch nur das einfachste eBook herzustellen, dann schaffe ich das halt selbst: mit einem Teppichmesser, ScanSnap S1500, Abbyy FineReader, iPhone, pdfasm, Dropbox und GoodReader.

Schritt 1: Ich zerschneide das Lektionsbuch und das Grammatik-Beiheft mit dem Teppichmesser.

image

Schritt 2: Ich scanne die Seiten mit ScanSnap “Schnappi” S1500. Ein Hoch auf das papierlose Büro! Das Resultat sind PDF-Dateien. Da die Seiten als Bilder gescannt wurden, sind die Dateien natürlich sehr groß. 30 KB für 76 Seiten Grammatik-Beiheft. Deshalb zerlege ich das Lektionsbuch in Blöcke (Kapitel 1-3, Vokabeln + Index usw.).

image

Schritt 3: Ich lasse die Abbyy FineReader als OCR Texterkennung über die PDFs laufen, damit aus den Bildern echte Texte werden und ich in den PDFs suchen und markieren kann. (Abbyy liegt übrigens dem “Schnappi” bei.)

image

Schritt 4: Ich fotografiere noch einige Seiten des Lektionsbuches mit dem iPhone, die ich nicht durch den Scanner schieben konnte. Ist halt ein Hardcover, das Schulalltag standhalten muss. Die Photo-App meiner Wahl: Camera+

image

Schritt 5: Ich bündele einige PDFs mit den fotografierten Seiten, damit der Look hübsch einheitlich ist. pdfasm ist mein Freund – auch wenn die Bedienung etwas intuitiver hätte sein können. Da waren wieder mal Techniker die Gestalter der Benutzeroberfläche.

image

Schritt 6: Ich lege die PDFs in meine Dropbox, um von allen Devices (Laptop, iPad, iPhone) darauf zugreifen zu können. (Hoppla, aufpassen, damit die PDFs nicht ins öffentliche Verzeichnis geraten und ich durch Unachtsamkeit einen Link darauf in die Welt sende, auf dass auch noch andere Schüler, Eltern, Lehrer ein elektronisches Découvertes in die Hand bekommen.)

image

Schritt 7: Schließlich werfe ich den GoodReader auf dem iPad an, um mit der Lektüre der ersten Lektion zu beginnen.

image

Zeitaufwand für diese Aktion? Hm… ich habe es nicht gestoppt, aber ich schätze mal, dass es in Summe 2 Stunden gedauert hat. Zeitaufwendig war dabei allerdings nur die Texterkennung. Hat mich aber nicht gestört. Abbyy hat ja still vor sich hingepusselt im Hintergrund, während ich am Rechner weiterarbeiten konnte.

Fazit

Das war gar nicht so schwer. Den Verlag hätte es kaum mehr Aufwand gekostet bei besserem Ergebnis (kleinere PDFs, weniger Stückelung des Inhalts). Der hat den ganzen Content ja in Quark Xpress oder so und könnte ihn ganz leicht nach PDF exportieren.

Warum tut man das nicht? Ich kann nur einen Grund erkennen: ängstliche Unsicherheit. “Oh, mein Gott, was alles passieren könnte, wenn erstmal so ein PDF auf einer CD – nein, zu teuer! – oder im Internet auf unserer Homepage verfügbar wäre. Kriminelle Kinder könnten es verbreiten! Unsere Schulbuchverkaufe würden ins Bodenlose fallen. In 2 Jahren wären wir pleite. Arrghhhh! Das darf nicht passieren!” (Besonders verständlich ist so eine Denke natürlich bei Schulbuchverlagen, die garantierte Verkäufe dadurch haben, dass ihre Lehrwerke verbindlich durch Schulen oder Eltern in Papierform angeschafft werden müssen.)

So stelle ich mir die Verlagsredaktion vor – bis mir jemand plausibel macht, dass es ganz andere, mir als Leser total verständliche Gründe gibt, meine Lesegewohnheit nicht zu bedienen, obwohl es technisch möglich ist. Wenn dies also ein Verlagsmitarbeiter lesen sollte, möge er mich über solche Gründe bitte ins Bild setzen.

imageNun, gut: Wie ich festgestellt habe, kann mir das Verhalten der Verlage ziemlich egal sein. Mit relativ wenig Aufwand habe ich mir mein eBook in “good enough” Qualität selbst hergestellt. Nächstes Mal mache ich das mit meiner Tochter zusammen. Daraus wird dann ein kleiner Event, die jährliche “School Book Slashing Party” oder so :-) (Vielleicht eine Anregung für Pädagogen, um Kindern eine Möglichkeit zum Ausagieren von Aggressionen zu bieten? Mit Teppichmessern auf Schülbücher statt auf Mitschüler oder später Flugzeugpiloten?)

In jedem Fall habe ich schon ein zweites Buch geordert, das ich mit dem Teppichmesser und den anderen Werkzeugen in ein eBook transformieren werde. Diesmal ist es ein Philosophiebuch: Das Prinzip Verantwortung. Suhrkamp verweigert hier die Veröffentlichung als eBook. Ob man meint, eine Existenz als eBook sei einem solchen Inhalt oder dem Klassiker-Status nicht würdig? Hm…

Naja, mir egal. Das Teppichmesser liegt schon bereit. Bei dem Buch wird es auch einfacher. Ist ein Paperback. Vielleicht kann ich das sogar komplett in Text umwandeln.

Aber nun kommen erstmal Sie: Machen Sie mit bei der großen Buchtransformation! Welchen Titel “rippen” Sie, um ihm die ewige Jugend als eBook zu schenken?

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat…