Follow my new blog

Sonntag, 20. März 2011

Flow-Orientation für´s Unternehmen - Teil 1, Theory of Constraints

Bei der Softwareentwicklung geht es immer um Geld. Das mag unschön klingen, ist aber nicht zu ändern. Jeder, der sein Geld mit der Entwicklung von Software verdient, muss mit seiner Software dazu beitragen, dass dieses Geld auch reinkommt. Alles, was wir in der Softwareentwicklung tun, muss sich deshalb die Frage gefallen lassen, ob es dem Ziel des Geldverdienens nützt.

Oberstes Ziel eines for-profit Unternehmens ist es, Geld heute zu verdienen und in der Zukunft.

Wenn ich an der Softwareentwicklung etwas verändern will durch Einführung der Methode ABC oder der Technologie XYZ, dann muss ich mir die Frage gefallen lassen, “Bringt das denn etwas?” D.h. ich muss zeigen, dass ABC oder XYZ einen positiven Einfluss auf das Geld hat.

Wie ich nicht nur mit den Prinzipien und Praktien der Clean Code Developer Initiative feststellen durfte, ist das nicht einfach. Entwickler finden die CCD-Bausteine meist wertvoll – das Management hingegen ist nicht so leicht zu überzeugen. “Was bringt es denn, so zu entwickeln?”

Tja… was bringt es denn? Meine persönliche Überzeugung mag noch so groß sein, ein positives Gefühl reicht nicht, um grünes Licht vom Management zu bekommen. Wer verkaufen will, der muss das also im Rahmen der Logik des Käufers tun. Ich muss mir also Gedanken darüber machen, wie es dazu kommt, dass Geld mit Software verdient wird. Erst wenn dich das verstehe, habe ich einen Ansatzpunkt für ein vernünftiges Gespräch mit einem Entscheider.

Leider habe ich kein BWL studiert. Ich bin kein MBA, kein Buchhalter, kein Controller. Meine Annäherung an die “Gelddenke” erfolgt daher aus anderer Richtung. Weil ich bei der Softwareentwicklung gerade den Hammer Flow-Orientation gefunden habe, schaue ich einfach mal, ob das Thema Geld auch ein Nagel ist, den ich damit einschlagen kann – ohne mir auf die Finger zu hauen ;-) Erstmal ein Werkzeug nehmen, das ich schon habe, bevor ich mich nach anderen umschauen, die ich noch lernen muss.

Flüssig Geld verdienen

Es geht also ums Geld verdienen. Wie hängt denn das mit Software zusammen? Ich sage mal ganz naiv, das sieht grundsätzlich so aus:

image

Softwareentwicklung stellt Software in Form von Releases her – und der Vertrieb verkauft die als “Packerl”.

Geld mit Software wird also in einem einem Fluss, einem Prozess verdient. Der hat mindestens und aus großer Flughöhe zwei Schritte. Und solange ich nicht den zweiten Schritt in den Blick nehme, kann ich eigentlich mit keinem Entscheider sprechen.

Das Problem jedes Technikers, der mit einem Manager spricht, wird an diesem Diagramm deutlich. Techniker und Manager haben unterschiedliche Perspektiven:

image

Und ich würde sogar noch weitergehen und sagen, die beiden haben nicht einmal dieselbe Vorstellung vom Gesamtsystem:

image

Und so können sie nicht zueinander kommen. Oder nur schwer. Ist doch eigentlich verständlich, oder?

Wenn ich also in einer Organisation etwas verändern will, dann tue ich gut daran…

  1. …alle Stakeholder an einen Tisch zu bringen; für CCD wären das zum Beispiel mindestens Entwickler und Entscheider.
  2. …sicherzustellen, dass die Stakeholder ein gemeinsames Bild von der Organisation haben.
  3. …dass das gemeinsame Bild in irgendeiner Form an das Ziel gekoppelt ist.

Mit einer flow-orientierten Sicht, einem möglichst vollständigen Bild des Prozesses, bei dem am Ende Geld verdient wird, scheint mir das am einfachsten möglich. Ein Flow macht klar, welche Schritte zu tun sind hin auf das Geld und wie die Zusammenhänge sind.

Geld wird mit Software verdient durch die Kooperation von Rollen. Vertrieb allein nützt nichts, Entwicklung allein nützt auch nichts. Kooperation ist nötig. Und ein Verständnis dafür, wie Produktion in solchen Prozesse auf ein Ziel hin optimiert werden kann.

Produktionsprozesse optimieren

Bevor ich meine Sicht des Softwareproduktionsprozesses konkretisiere ein kurzer Ausflug ins Allgemeine; meine Erklärung basiert dabei auf der Theory of Constraints von Eliyahu M. Goldratts, so wie ich sie derzeit verstehe.

Wie kriege ich also eigentlich aus einem Prozess das meiste raus? Wie kann ich mit einem Prozess am meisten Geld verdienen?

Hier ein allgemeiner Prozess, der in 3 Schritten etwas herstellt und an den Kunden bringt:

image

Erst wenn das “Packerl” über den Tisch zum Kunden gewandert ist, fließt Geld. “Material” [1] in Form eines Produktes fließt aus dem Prozess in den Markt und Geld fließt zurück in den Prozess bzw. die Organisation, in der er fließt. Das Geld des Marktes hält den Prozess am Leben.

Es ist ein Transformationskreislauf. “Material” wird zu Geld wird zu “Material” wird zu Geld usw. usf.

Jetzt zum Grundsatz #1 solcher Geldverdienprozesse: Geld ist erst verdient, wenn der Kunde gekauft hat.

Hört sich trivial an, wird in Diskussionen aber schnell vergessen. Der Börsenspekulant weiß: Nicht realisierte Gewinne sind keine Gewinne. Soll heißten: Von günstigen Gelegenheiten zu reden, bringt nichts. Wer sie nicht wahrnimmt, hat nichts davon. Erfolgreich ist nur, wer sie nutzt.

Auf den Geldverdienprozess angewandt bedeutet das: Ein “Packerl” nützt nichts, solange es nicht verkauft ist. Jedes “Packerl” auf Lager ist gebundenes Kapital. Dito jedes X und Y: Zwischenprodukte sind gebundenes Kapital. Geld, das in den Prozess geflossen ist, wurde umgewandelt in “Material”. Damit ist das Geld weg – und solange kein “Packerl” verkauft wurde, kommt dafür kein Geld zurück.

Es heißt also verkaufen, verkaufen, verkaufen. Dann kommt mehr Geld rein. Und das bedeutet, produzieren, produzieren, produzieren. Oder?

Grundsatz #2: Man kann nur soviel verkaufen, wie der Markt bereit ist abzunehmen.

Am Markt herrscht für “Packerl” ein bestimmter Bedarf. Alle Hersteller von “Packerln” können zusammen nur soviel verkaufen, bis die Nachfrage befriedigt ist. Wer mehr produziert als nachgefragt (oder absetzbar) ist, der hat überproduziert und damit Geld in unverkauftem/unverkäuflichem “Material” gebunden.

Eine der vornehmsten Aufgaben jedes Managers muss es daher sein, die Produktion so zu steuern, dass keine Überproduktion entsteht. Das bedeutet, die Produktionsleistung wird vom Markt bestimmt. Der Markt zieht sozusagen an der Produktion. Oder der Markt kann als ein Unterdruck angesehen werden, der “Packerl” ansaugt.

Die Steuerung eines Produktionsschritts n geht mithin vom ihm folgenden Schritt n+1 aus! Wenn der Markt pro Monat 10 “Packerl” braucht, dann sollten auch nur 10 produziert werden. Werden mehr produziert, ist das Geldverschwendung, weil ab dem 11. “Packerl” nicht mehr verkauft werden kann, aber Geld in der Herstellung geflossen ist. Geld wird also verbrannt. Werden weniger produziert, ist das Geldverschenkung, weil existierende Gelegenheiten zum Geldverdienen nicht genutzt werden.

image

Natürlich ist es eine rechte Kunst, den Bedarf abzuschätzen und die Produktion darauf einzustellen. Das ist nicht leicht. Aber darum geht es mir hier nicht. Diese Kunst zu lernen lohnt nämlich erst, wenn man die Implikationen verstanden hat.

Grundsatz #3: Downstream Bedarf bestimmt upstream Produktion in allen Teilen eines Prozesses.

Dass man gegenüber dem Markt nicht überproduzieren sollte, leuchtet schnell ein. Aber es ist wichtig zu verstehen, dass das nicht nur für das Verhältnis Produktion:Markt gilt, sondern für jede Folge von Prozessschritten.

Ein Folgeschritt n+1 hat pro Zeiteinheit immer eine bestimmte Kapazität, mit der er “Material” vom vorhergehenden Schritt n aufnehmen kann. Beim Markt heißt diese Kapazität Bedarf. Es gibt sie jedoch nicht nur dort, sondern bei jedem Prozessschritt.

Das bedeutet, dass sich die Produktionsleistung eines Prozesses auf jeder Ebene und in jedem Schritt immer am Bedarf ausrichtet, am Bedarf des Marktes bzw. an der Kapazität von Folgeschritten. Oder anders und radikal ausgedrückt:

Produktionsschritte produzieren nicht soviel sie können pro Zeiteinheit, sondern nur soviel wie vom Folgeschritt abgenommen werden kann.

Das widerspricht weithin gepflegter Denkungsart. Die fordert nämlich, dass jeder Schritt immer möglichst nahe an seiner Kapazitätsgrenze läuft. Eine stillstehende Maschine, ein Zeitung lesender Entwickler… das darf nicht sein! Jedes und jeder sollte möglichst immer mit “Materialverarbeitung” bzw. “Materialproduktion” beschäftigt sein.

Aber das ist falsch, weil es zu Kapitalbindung in Zwischenerzeugnissen führt. Wenn Produktionsschritt n mehr in einer Zeiteinheit herstellt als Schritt n+1 weiterverarbeiten kann, dann entsteht eine Halde vor n+1.

n ist dann zwar fein raus, weil dort die Kapazität ausgeschöpft wird. n+1 ist ebenfalls fein raus, weil auch dort an der Kapazitätsgrenze gefahren wird. Der lokale, enge Blick auf die Prozessschritt und ihre Kapazitätsauslastung sieht also ein gutes Ergebnis.

Aber was ist mit der Halde zwischen n und n+1? Nicht nur in Anschaffung und Betrieb von n und n+1 steckt Geld; Geld steckt auch in der Überproduktion, die zwischen Schritten auf Halde liegt.

image

Consumer, d.h. downstream Produktionsschritte steuern also den Output von Producern, d.h. upstream Produktionsschritten.

Wenn denn alle Produktionsschritte die gleiche Kapazität hätten, dann würde sich die Produktion ganz einfach in allen Teilen nach dem Markt richten. Der Markt würde mit seinem Bedarf M ziehen/ansaugen und der letzte Produktionsschritt m würde mit seiner Kapazität Km darauf eingestellt. Das würde dann zur Folge haben, dass sich m-1 in seinem Ausstoß Am-1 (und also auch früher oder später in seiner Kapazität Km-1) nach m richtete. Das würde dann zur Folge haben, dass sich m-2 nach m-1 richtet usw. usf.

Am Ende würde schönste Eintracht herrschen, weil M=Ki und Ai=Ki+1 gälten. Alles wäre im Gleichgewicht, nein, alles wäre gleich: Bedarf, Kapazitäten und Ausstöße.

Aber so ist die Welt leider nicht. Kapazitäten lassen sich nicht gleichmachen. Und der Markt bewegt sich dauernd. Das bedeutet, es gibt Kapazitätsunterschiede bei den Schritten im Geldverdienprozess.

Daraus folgt nach Grundsatz #3, dass nicht alle Produktionsschritte ihre Kapazität ausschöpfen sollten. Sonst läuft das Gesamtsystem Gefahr, Geld durch Überproduktion zu verschwenden. Es gilt vielmehr:

Grundsatz #4: Der Ausstoß eines Prozesses richtet sich nach dem Prozessschritt mit der geringsten Kapazität. Dieser Schritt ist der Engpass des Prozesses.

Wieviel “Packerl” kann der folgende Prozess pro Zeiteinheit an den Markt bringen?

image

Der Markt hat einen Bedarf M=4, die Kapazitäten sind Ka=3, Kb=2, Kc=4. An C soll es also nicht liegen, wenn der Markt nicht befriedigt wird; C kann mit der Nachfragte mithalten.

Aber C kann pro Zeiteinheit nur 2 Y von Produktionsschritt B bekommen. Egal wie C sich auch anstrengen mag, es bekommt nicht genügend Input, um seine Kapazität auszuspielen. B hungerte C gewissermaßen aus.

B auf der anderen Seite könnte von A 3 X pro Zeiteinheit bekommen – also 1 X mehr als es verarbeiten kann. B würde von A überschwemmt, vor B entstünde eine Halde mit gebundenem Kapital.

Es hilft also nichts: Der Bedarf kann sein, wie er will, es können nicht mehr “Packerl” hergestellt werden, als die, zu denen Produktionsschritt B pro Zeiteinheit seinen Beitrag leisten kann. Und das sind 2 X, die am Ende zu 2 “Packerl” werden.

Der Ausstoß eines Prozesses richtet sich nach der Kapazität des Engpasses: A=Ke. Auf den Engpass folgende Produktionsschritte werden unterfordert, vor ihm liegende müssen gedrosselt werden. Andere Produktionseinschritte als den Engpass oberhalb dessen Kapazität zu fahren, bringt einfach nichts. Das würde nur geschäftig aussehen, aber nicht zum Betriebsergebnis beitragen. Im Gegenteil: Vorgelagerte Schritte würden eine Kapitalbindung durch Haldenbildung erzeugen. Das wäre Geldverschwendung.

Das bedeutet im Umkehrschluss:

Grundsatz #5: Investionen sind nur lohnend, wenn sie sich auf die Beseitigung des Engpasses konzentrieren.

Solange der Markt mehr Bedarf hat, als die Produktion hergibt, liegt der Engpass im Prozess. Wer den Ausstoß vergrößern will, um mehr vom Bedarfskuchen zu bekommen, der muss sich auf die Erweiterung des Engapsses konzentrieren. Investionen in A oder C im vorigen Diagramm lohnen nicht. Ihre Kapazität, die ja ohnehin schon größer ist als die von B, zu erhöhen, wäre Geldverschwendung.

Leider gehen viele Unternehmen jedoch diesen Weg. Sie haben einen engen Blick, der jeden Produktionsschritt nur für sich sieht. Und sie denken, mehr Kapazität und Kapazitätsauslastung wären per se wünschenswert. Leider gilt für abhängige Prozessschritte jedoch nicht, dass optimale Einzelsschritte zu einem optimalen Gesamtergebnis führen.

Abteilungsdenken, Budgetdenken und Managementhierarchien leisten solchem Denken allerdings Vorschub. Und so ist es kein Wunder, dass viele Unternehmen Geld in unnützen Optimierungen versenken. Es wird zwar getan und gemacht… aber es bringt nichts, weil es nicht am Engpass ansetzt.

Am Engpass anzusetzen ist natürlich auch schwer, wenn man keine rechte Vorstellung vom Produktionsprozess hat. Wer keinen Fluss zeichnet, auf dem seine Produkte von der Idee bis zum “Packerl” beim Kunden schippern, der kann sich nicht fragen, wo ein Flaschenhals sitzt. Der reagiert vielmehr immer nur erratisch auf Symptome die sie hier oder dort zeigen. Ein gezielter, systematischer Eingriff zur Optimierung des Gesamtsystems ist nicht möglich. Denn das Gesamtsystem liegt ja im Dunkeln.

Wer das Gesamtsystem jedoch kennt, der kann systematisch vorgehen:

  1. Engpass identifizieren
  2. Engpass ausreizen (Grundsatz #5)
  3. Nicht-Engpass Prozessschritte am Engpass ausrichten (Grundsatz #3)
  4. Enpass erweitern (Grundsatz #5)
  5. Zurück zu 1.

Nach Identifikation von B als Engpass oben, müsste zuerst sichergestellt werden, dass B auch wirklich seiner Kapazität gemäß pro Zeiteinheit Ausstoß produziert; es ist zu erreichen: Ab=Kb.

Dann müsste Aa=Kb eingestellt werden. An C kann man nicht viel tun, ohne die Kapazität zu verringern.

Und schließlich wäre zu prüfen, wie die Kapazität von B erhöht werden könnte. Würden mehr Personal oder mehr Maschinen helfen? Oder eine Maßnahme, um die Produktivität der vorhandenen Ressourcen zu erhöhen?

Zwischenstand

Wenn ich der Softwareentwicklung ein Angebot machen will, dann funktioniert das letztlich nur, wenn Entwickler, Entscheider und ich ein gemeinsames Verständnis vom Gesamtsystem haben, in dem die Softwareentwicklung eine Rolle spielt. Die Frage ist zu klären, wie die Softwareentwicklung zum Geldverdienen beiträgt.

Wie aber sieht so ein Gesamtsystem aus? Ich finde eine Beschreibung mit Flüssen genauso einfach wie hilfreich. Und die Theory of Constraints (TOC) zeigt mir, wie solche Produktionsflüsse auf das Ziel “Geld verdienen heute und in der Zukunft” hin optimiert werden können.

Hier konnte ich die TOC nur anreißen. Es ging mir darum, eine Grundlage für weitere Erklärungen zu legen, die sich konkreter auf die Softwareproduktion beziehen. Ausgelassen habe ich z.B. die Themen Puffer und Maßzahlen. Aber wer mehr erfahren will, der kann viel darüber lesen. Ich finde z.B. diese Beschreibung online von Yougman sehr gut lesbar. Leider gibt es dazu kein handliches eBook, so dass die 500+ Seiten im Browser gelesen werden müssen. Wer es lieber kürzer und in Prosa haben möchte – um es z.B. auch dem Chef zu schenken –, der tut sich etwas Gutes mit dem Roman “Das Ziel” vom Erfinder persönlich.

Im nächsten Teil zur Flow-Orientation für´s Unternehmen baue ich auf der TOC auf und beschreibe meine Vorstellung eines grundsätzlichen Softwareproduktionsprozesses. Und danach unterhalten wir uns über Engpässe…

 

[1] “Material” schreibe ich in Anführungsstrichen, weil ich damit nicht nur Handfestes wie Stahl oder Holz meine, sondern auch Wissen und Software. “Material” ist etwas, aus dem man etwas herstellen kann, wofür Kunden bereit sind, Geld zu bezahlen.

Donnerstag, 17. März 2011

Fit für FitNesse

imageFitNesse ist ein hübsches Framework für Akzeptanztests. Ist zwar Java-basiert mit seinem eigenen Web-Server, aber der Einsatz ist auch für .NET möglich. Gojko Adzic hat dazu schon vor längerer Zeit ein Buch geschrieben – Test Driven Development with FitNesse -, dass auch kostenlos als eBook verfügbar ist.

Leider hat sich der Einstieg dann aber doch nicht so flüssig gestaltet, wie ich es gern gehabt hätte. Die grundsätzliche Installation ist simpel:

  1. Java VM installieren; Download von http://www.java.com/de/download/
  2. FitNesse Server herunterladen von http://fitnesse.org/FrontPage.FitNesseDevelopment.DownLoad. Er kommt als .jar-Datei, für das Sie am besten ein FitNesse-Verzeichnis anlegen, z.B. C:\FitNesse
  3. FitSharp herunterladen von http://fitsharp.github.com/. Das ist ein Framework wie NUnit, mit dem Sie .NET-Klassen zu FitNesse-Fixtures machen können. Die FitSharp Assemblies legen Sie am besten im FitNesse-Verzeichnis ab, z.B. unter C:\FitNesse\FitSharp.
  4. FitNesse-Server starten. Ich habe mir dazu einen einzeiligen Batch gebastelt, der im FitNesse-Verzeichnis liegt und den Server auf Port 8888 startet:

    java -jar fitnesse.jar -p 8888

Das Ergebnis einer soweit erfolgreichen Installation ist ein laufender Server:

image

Auf den können Sie über den Browser mit Adresse localhost:8888 zugreifen:

image

Soweit lief bei mir auch alles gut. Knifflig wurde es, als ich dann eine C# Testfixture aufrufen wollte. Ich hatte sie nach Gojkos Beispiel wie folgt geschnitzt:

image

Die FitSharp Assemblies fit.dll und fitSharp.dll musste ich referenzieren (am besten aus einem projektlokalen Verzeichnis) und die Fixture-Klasse von ColumnFixture ableiten. Weitere Details entnehmen Sie am besten Gojkos Buch.

Ein FitNesse-Test war ebenfalls einfach aufgesetzt:

image

Nur leider zeigte der nicht den gewünschten Erfolg. Nach Start über das FitNesse-Dashboard passierte schlicht nichts :-( Nicht mal eine Fehlermeldung erschien. Der Test war angestoßen worden… aber “kam nicht zurück”. FitNesse schien zu hängen.

Tip #1: Um mehr Infos über einen (abstürzenden) Test zu erhalten, nicht mit dem FitSharp Runner.exe starten, sondern mit RunnerW.exe:

!define TEST_RUNNER {fitsharp\RunnerW.exe}

Der Grund für das Hängen war, dass durch eine Security Policy Änderung bei .NET 4 der Runner-Prozess Libraries nicht einfach so laden konnte. Ob es daran lag, dass meine Test-Assembly nicht auch im FitSharp-Verzeichnis lag (wie in Gojkos Beispiel), weiß ich nicht. Diesen Grundsatz wollte ich jedenfalls nicht aufgeben.

Tip #2: Um das Laden der nötigen Assemblies für den Runner zu ermöglichen, einfach zwei App.Config-Dateien im FitSharp-Verzeichnis anlegen. Runner.exe.config und RunnerW.exe.config:

<configuration>
   <runtime>
      <loadFromRemoteSources enabled="true"/>
   </runtime>
</configuration>

Mit der Konfigurationsdatei lief dann der Runner, meldete aber, dass er meine Testklasse nicht finden können. Dafür die Ursache zu finden, hat mich dann einige Zeit gekostet. Google war keine Hilfe. Ich hatte ja auch alles Formal richtig gemacht.

Am Ende stellte sich heraus, dass FitNesse oder FitSharp ein Problem mit dem Namespace der Testklasse hatte. Dessen Name lautete ursprünglich “spike_fittest”. Als ich ihn dann nach “fittest” umbenannt hatte, lief der Test durch.

image

Tip #3: Sonderzeichen in Namen von Namensräume oder Testklassen vermeiden, allemal einen Unterstrich “_”. (Umlaute scheinen allerdings kein Problem zu machen und sogar falsche Groß/Kleinschreibung wird toleriert.)

Schließlich noch ein Hinweis auf FitSharp Suite-Dateien. Zwischendurch schien es, dass “etwas” besser wurde, als ich eine solche testspezifische Datei mit Runner-Konfigurationsdaten im Test referenzierte. Meine Suite-Datei suite.config.xml sah so aus:

<suiteConfig>
    <ApplicationUnderTest>
        <addNamespace>fitlibrary</addNamespace>
    </ApplicationUnderTest>
</suiteConfig>

Eingebunden hatte ich sie in den Test so:

image

Heute, beim Schreiben dieses Artikels, konnte ich allerdings keinen Vorteil mehr darin erkennen. Auch ohne Referenz dieser Suite lief der Test problemlos. Merkwürdig… Aber egal. Ich denke, ein Hinweis auf Suite-Dateien ist trotzdem nützlich.

Ich hoffe, mit diesem kleinen Erfahrungsbericht mache ich Ihnen den Einstieg in FitNesse mit .NET ein bisschen einfacher. Mir gefällt der Framework als Grundlage für Softwareprüfstände. Bevor Sie einen Prüfstand komplett selbst aufsetzen, schauen Sie sich einmal FitNesse an. Damit können Sie auch mal einen Kunden auf Ihren Code loslassen.

Sonntag, 13. März 2011

It is all design – really?

image

Uncle Bob hat unlängst getwittert: “It is all design.” Und dafür hat er auch großes Lob aus Deutschland bekommen.

Ich frage mich hingegen: Wenn denn alles Design ist, warum sollte ich dann dieses Design in textuellem Quellcode durchführen?

Ja, ja, ich weiß, dass Jack W. Reeves schon in den 1990ern “bewiesen” hat, dass Programmierung nicht Herstellung, sondern Design ist. Dem stimme ich im Vergleich zum Baugewerbe oder der Autoproduktion auch zu. Softwareentwickler heißen nicht umsonst Entwickler und nicht Hersteller. Softwareentwicklung ist eine sehr kreative Tätigkeit. Ein Auto zu produzieren oder ein Haus hochzuziehen hingegen nicht. Da wird weitgehend nur ein Plan abgearbeitet. Softwareentwicklung hingegen ist Planerfindung.

Insofern produziert Codieren natürlich Design. “It is all design” könnte also wahr sein.

Aber: Design verhält sich zu Produktion wie Landkarte zu Terrain – das eine ist Abstraktion, das andere Detail, Konkretes. Was nun aber, wenn das Design so kompliziert ist, dass man es nicht einfach darlegen kann, wenn es selbst ein Terrain ist. Man kann es dann nicht runterschreiben. Selbst eine Landkarte will recherchiert sein. Sie kommt aus vielen Quellen zusammen und ist am Ende doch nur ein recht unkreatives Abbild der Realität. Software aber kann nicht irgendwo abgemalt werden. Software als Design muss erdacht werden. Code, der selbst ja nur ein Produktionsplan ist, muss selbst geplant, entworfen werden.

Und damit sind wir beim terminologischen Dilemma. Es ist eben nicht alles zu Design gesagt, wenn man konstatiert, Code sei Design. Man muss dazu sagen, was denn Design überhaupt seiner Natur nach sei. Man muss den allgemeinen Fall in den Blick nehmen:

Design ist ein Plan für etwas Konkretes.

Wenn etwas Konkretes kompliziert ist, dann kann man es nicht “einfach so” herstellen, sondern dann muss man sich darüber vorher mal Gedanken machen. Allemal, wenn das Konkrete etwas Neues ist.

Design braucht also selbst durchaus Design. Und das Design dann womöglich auch wieder usw. usf. Design ist mithin nur relativ, d.h. in Gegenüberstellung zu etwas Konkretem sein Vorläufer:

Design –> Konkretes

Nimmt man jedoch das Design selbst in den Blick, wird es ebenfalls etwas Konkretes, d.h. ein Produkt, das designt werden kann:

Design' –> Design

Und auch das Design des Designs könnte kompliziert sein, so dass es ein Design brauchen könnte:

Design'' –> Design'

So ergibt sich eine Hierarchie von Designs mit dem endgültig Konkreten als Ziel:

Design''(Design'(Design(Konkretes)))

Das halte ich für ein allgemeines Bild vom Verhältnis von Design zu Produkt. Der Entwurf geht dem Konkreten voraus. Und was bedeutet das für die Softwareentwicklung? Deren endgültig Konkretes ist klar: ausführbarer Code. Quellcode ist laut Reeves ein Design dafür. Uncle Bob sieht das ähnlich, wenn er sagt, Testing und Refactoring seien Designtätigkeiten (weil sie Quellcode formen). Für Sie ist die Hierarchie deshalb nur zweistufig:

Quellcode(Maschinencode)

Ich hingegen behaupte mal, dass das eine ganz und gar unnötig beschränkende Sichtweise ist. Wenn wir sie als Ende der Fahnenstange ansehen, dann müssen wir uns bei der Softwareentwicklung nur noch stärker bemühen, den textuellen Quellcode in den Griff zu bekommen. Refactoring-Tools müssen besser werden, Test-Tools müssen besser werden, die Ausbildung zum Quellcodeschmied muss besser werden… dann wird es schon gehen.

In den letzten 60 Jahren ist die Softwareentwicklung aber nicht besser geworden, weil sie auf der jeweils höchsten Abstraktionsstufe stehengeblieben wäre. Wir entwickeln heute natürlich Komplizierteres viel schneller als vor 60 Jahren. Nur tun wir das nicht mit besseren Maschinencode-Tools. Die Hierarchie sieht vielmehr so aus:

OO-3GL(IL-Code(Machinencode))

Oder sie sah sogar einmal so aus:

OO-3GL(3GL(Assembler(Maschinencode)))

Wir sind voran gekommen, weil wir uns auf höhere Abstraktionsebenen geschwungen haben!

Statt besser mit Maschinencode umgehen zu können, lassen wir ihn lieber generieren. Statt besser mit Assembler umgehen zu können, lassen wir den Code lieber generieren. Statt C zu schreiben, lassen wir den Code von einem C++-Compiler generieren. (Zumindest habe ich das noch 1990 erlebt. Es war aber eine vorübergehende Phase, die hier jedoch eine schöne Illustration des generellen Trends gibt.)

Warum also sollten wir da stehenbleiben? Was ist so heilig an der Hierarchie

C#(IL-Code(Maschinencode))

oder

Java(JBC(Maschinencode))

Ist denn der Softwareentwicklung die Phantasie ausgegangen? Haben sich alle im Schmerz eingerichtet, den es macht, 3GL-Code direkt zu schreiben und auch noch fehlerfrei und evolvierbar zu halten?

Nein, ich finde Uncle Bobs Position uninspirier(t/end). Er hat in “Clean Code” das Richtige gesagt und Gutes bewirkt. Danke dafür! Bei Refactoring und Test-first (und seinem Hobby Clojure) jedoch stehenzubleiben und auszurufen “It is all design.”, das finde ich zuwenig.

Warum nicht einen Schritt weiter denken? Wer füllt das Fragezeichen in dieser Hierarchie mit Leben:

<?>(C#(IL-Code(Maschinencode)))

Ist das der Platz für UML? Das glaube ich nicht. UML führt nicht direkt zu 3GL-Code – oder wenn, dann nur sehr umständlich. UML ist de facto ein Werkzeug, das vor allem der Beruhigung von Management dient. Regelmäßig geben auf Entwicklerveranstaltungen (in der .NET-Community) höchstens 15% der Anwesenden an, dass sie mit UML entwerfen.

Oder ist das der Platz für DSLs? Martin Fowler scheint das zu glauben, wenn er nicht gerade derselben Meinung wie Uncle Bob ist. Dagegen spricht für mich, dass DSLs selbst zunächst entworfen werden müssen, bevor sie helfen, 3GL-Code zu entwerfen:

Meta-DSL(DSL(C#(IL-Code(Maschinencode))))

Das scheint mir einer breiten Adoption entgegen zu stehen. Die Konzepte und Notation auf einer Ebene anzuwenden ist kompliziert genug wie wir am Beispiel C# sehen. Da hilft es wenig, Entwicklermassen zu empfehlen, zuerst Kompetenz auf einer Meta-Ebene zu erwerben, um immer wieder neue Konzepte und Notationen zu entwickeln, die dann erst zum Entwurf von Quellcode eingesetzt werden können. Der begrenzte Erfolg von MDSD scheint mir diese Sicht zu bestätigen.

Und nun? Ich kann leider nicht umhin, einen Anwärter auf die Position einer Entwurfssprache für 3GL in Flow-Design zu sehen. (Wer hätte das gedacht? ;-)

Flow-Design(C#(IL-Code(Maschinencode)))

Die bisher entwickelten Konzepte und die Formensprache für Flow-Design ist einfach zu erlernen und deckt viele typische Entwurfsbedürfnisse für Quellcode ab. Es gibt klare Übersetzungsregeln für Flow-Design Entwürfe in 3GL-Code. Die Ergebnisse sind übersichtlich und skalieren. Flow-Design ist nicht domänenspezifisch; einmaliger Lernaufwand zahlt sich also in allen Problemlebenslagen aus.

Soweit mal mein kühnes Statement :-) Aber ich bin natürlich offen dafür, dass man mir die Nutzlosigkeit einer Design-Abstraktionsebene oberhalb von Quellcode erklärt. Einstweilen erlaube ich mir jedoch, Uncle Bob bewusst misszuverstehen. Ich sage auch: It is all design. Aber ich meine damit etwas anderes. Für mich findet Design auf einer höheren Ebene statt als “Quellcoderumgeschubse”.

Sonntag, 27. Februar 2011

Beziehungsratgeber für Softwareentwickler

Komplexität vergällt uns das Programmieren. Unwartbarkeit ist ein Komplexitätsproblem. Aber was macht die Komplexität in Software aus?

Für mich ist etwas komplex, wenn ich es durch Nachdenken nicht verstehen kann.

Solange etwas kompliziert ist, hilft Nachdenken. Beispiel: Der Boyer-Moore Algorithmus zur Zeichenkettensuche ist kompliziert (zumindest für mich). Ich verstehe ihn nicht sogleich, aber wenn ich die Beschreibung ein paar Mal lese und darüber nachdenke, dann blicke ich durch und kann ihn implementieren.

Insofern sollte Software eigentlich gar nicht komplex sein. Programme sind doch nur Ansammlungen von Algorithmen, die jeder für sich höchstens kompliziert sind.

Das wäre wohl korrekt, wenn da nicht Beziehungen bestünden. Die einzelnen “nur” komplizierten Teile einer Software sind miteinander verbunden. Sie stehen in Abhängigkeitsverhältnissen. Und das macht ihre Summe mehr als kompliziert, nämlich komplex.

imageDenn wo viele, gar unüberschaubar viele Beziehungen bestehen, da ist unklar, wie sich Veränderungen an einem Teil über die Beziehungen auf andere Teile auswirken. Mit unendlich viel Zeit, wäre das zwar herauszubekommen – doch wer hat die schon. Nachdenken würde also zwar grundsätzlich helfen, nur nicht in der verfügbaren Zeit. Somit ist Software nicht nur eine komplizierte Summe aus mehr oder weniger komplizierten Teilen, sondern ein komplexes Ganzes.

Also: Beziehungen zwischen Funktionseinheiten machen Software komplex. Je mehr es sind, je undurchsichtiger sie sind, desto komplexer das Ganze. Das bedeutet: Softwareentwicklung muss sehr bewusst mit Beziehungen zwischen Funktionseinheiten umgehen, um die Komplexität nicht ungewollt wachsen zu lassen.

Hier deshalb ein paar Beziehungsratschläge:

Ratschlag #1: Beziehungen vermeiden

Wenn Beziehungen aus Kompliziertem Komplexes machen, dann scheint es angeraten, auf sie zu verzichten, wo es nur geht. Denn ohne Beziehung keine (ungewollte) Ausbreitung von Änderungsnotwendigkeiten. Wenn Funktionseinheit A und B keine Beziehung haben, dann kann eine Veränderung an A keinen Effekt auf B haben.

image

Wo hingegen eine Beziehung besteht, da können sich Änderungen ausbreiten:

image

Hier ein Beispiel: Methode A() ruft Methode B() auf, um Daten zu beschaffen; identifiziert werden die Daten durch einen Schlüssel. A() ist also abhängig von B():

void A()
{
  var daten = B(“123”);
  …
}

void B(string schlüssel) {…}

Wenn nun B() seine Signatur ändert und statt einer Zeichenkette nur noch ganze Zahlen als Schlüssel akzeptiert, dann hat das Auswirkungen auf A().

Das ist eine recht simple Abhängigkeit und der Compiler erkennt, wo sie nicht erfüllt ist. Was aber, wenn die Abhängigkeit nicht syntaktischer Natur ist?

Nehmen wir an, B() liefert eine Zeichenkette der Form “a=1;b=2” zurück, aus der A() relevante Werte extrahieren soll. Dann zerlegt A() diese Zeichenkette sicherlich zunächst beim Trennzeichen “;”:

void A()
{
  var daten = B(“123”);
  foreach(string zuweisung in daten.Split(‘;’))
  {…}
}

Wenn jetzt B() die Wertzuweisungen in der Zeichenkette aber nicht mehr durch “;” trennt, sondern durch “,” oder beide Trennzeichen zulässt, dann hat das ebenfalls Auswirkungen auf A() – allerdings kann der Compiler nicht helfen, diese zu aufzustöbern. Es liegt eine tückische semantische oder logische Abhängigkeit vor.

Fazit: Sobald Beziehungen ins Spiel kommen, wird es bei der Softwareentwicklung ekelig. Vermeiden Sie daher Beziehungen oder minimieren Sie sie soweit es eben geht. Vorsicht vor allem vor logischen Beziehungen!

Ratschlag #2: Beziehungen syntaktisch machen

Nicht zu vermeidende Beziehungen sollten, wenn es irgend geht, syntaktisch sein und nicht logisch. Logische Abhängigkeiten machen das Ganze vor allem komplex, weil Werkzeuge nicht helfen, sie zu überblicken. Der Mensch muss sie “im Kopf haben”. Und wenn er das nicht hat, dann merk er erst zur Laufzeit, ob durch Änderungen an einer Funktionseinheit etwas bei anderen “verrutscht ist”.

Bezogen auf das obige Beispiel bedeutet das, die Rückgabe von Name-Wert-Paaren in Form eines String aus B() sollte ersetzt werden z.B. durch ein Dictionary. Das Wissen um Trennzeichen ist dann konzentriert auf B(); sollten sie sich ändern, hat das keine Auswirkungen mehr auf abhängige Funktionseinheiten wie A().

Strenge Typisierung ist also der erste Schritt zu einem anti-komplexen Beziehungsmanagement. Ein spezifischer Typ ist besser als ein allgemeiner. string reduziert logische Abhängigkeiten gegenüber object. Und HashTable reduziert sie noch weiter. Und Dictionary<string,int> reduziert sich noch weiter.

Abstrakte Datentypen (ADT) und Kapselung sind der nächste Schritt. Wieder in Bezug auf das Beispiel oben: Ein Dictionary<> ist zwar spezifischer als ein string, doch es lässt zum Beispiel zu, dass Werte überschrieben werden können. Vielleicht soll das jedoch nicht erlaubt sein für das, was zu Beginn als Zeichenkette kodiert war. Solange ein Dictionary<> “durch die Gegend geschoben wird”, existiert also noch eine subtile logische Abhängigkeit: die Funktionseinheiten, die mit den Daten umgehen, müssen wissen, dass sie Werte im Dictionary<> nicht überschreiben dürfen. Eine formale Prüfung, ob sie das auch beherzigen, gibt es aber nicht. Erst zur Laufzeit wird das sichtbar.

Besser ist es da, statt eines Standard-Dictionary einen spezielleren Datentyp zu implementieren, z.B. NonOverridableDictionary<TKey, TValue>. Die Beziehung zwischen A() und den Daten wird damit weiter syntaktisch formalisiert.

Fazit: Beziehungen, deren Gültigkeit der Compiler überprüfen kann, sind anderen vorzuziehen.

Ratschlag #3: Süchtige vereinfachen

Der Drache der Komplexität ist noch nicht erschlagen, wenn die Beziehungen so gering wie nötig und so syntaktisch wie möglich sind. Auch dann kann es noch Funktionseinheiten geben, die von vielen anderen abhängig sind. Es sind die unvermeidbar “Süchtigen”; sie brauchen viele andere Funktionseinheiten zu ihrem Glück.

image

Wenn Sie auf solch ein Beziehungsmuster stoßen, dann… ja, dann hilft nur eines: Halten Sie die süchtige Funktionseinheit so einfach wie möglich in Bezug auf die Funktionalität bzw. Domäne ihrer “Suchtmittel”.

Der Grund ist einfach: Wenn eine Funktionseinheit von vielen anderen abhängt, dann ist die Wahrscheinlichkeit sehr hoch, dass sich an einer Funktionseinheiten etwas ändert, die relevant für die Süchtige ist. “Irgendwas ist immer” könnte man sagen.

Solche Änderungen können sich aber nur fortsetzen entlang der Beziehungen (vom Unabhängigken zum Abhängigen), wenn der Abhängige kompliziert ist, d.h. große Angriffsfläche bietet. Denn Kompliziertheit geht gewöhnlich einher mit vielen logischen Abhängigkeiten.

Da logische Abhängigkeiten aber so schwer zu sehen sind, lautet der Rat, eher auf die Kompliziertheit der süchtigen Funktionseinheit zu achten, d.h. auf deren Umfang und den internen Aufbau. Der Umfang sollte klein, der interne Aufbau simpel sein.

Wenn sich nun an den komplizierten Funktionseinheiten etwas ändert, dann ist die Wahrscheinlichkeit, dass das eine Auswirkung auf das einfache Abhängige hat, gering.

image

Typisches Beispiel für ein solches Abhängigkeitsmuster sind DI Container. Bei ihnen werden viele Typen registriert, ihre Aufgabe ist sozusagen, maximal abhängig zu sein. Aber das macht nichts, weil DI Container sehr einfach sind in Bezug auf das, wozu sie Beziehungen haben. Sie wissen von den Domänen der Funktionseinheiten, die bei ihnen registriert werden, nichts.

Fazit: Je einfacher eine Funktionseinheit, desto größer kann die Zahl ihrer Abhängigkeiten sein.

Ratschlag #4: Promiskuitive vereinfachen

Das Gegenteil von süchtigen Funktionseinheiten, d.h. solchen, die von vielen anderen abhängen, sind solche, zu denen viele andere Abhängigkeitsbeziehungen haben. Es sind Promiskuitive mit großem Netzwerk. Fallen an ihnen Änderungen an, dann haben die potenziell ein großes Ausbreitungsgebiet.

Was können Sie dagegen tun? Machen Sie prosmikuitive Funktionseinheiten so simpel wie möglich in Bezug auf die Funktionalität der von ihnen abhängenden.

image

Das bedeutet, Änderungen setzen sich nicht so leicht entlang der Beziehungen fort. Vor allem aber: Es fallen Änderungen an der promiskuitiven Funktionseinheit gar nicht so häufig an. Denn was einfach ist, das sollte sich nicht so oft ändern.

Typisches Anti-Pattern für Promiskuitive sind “fette” Domänendatenmodelle. Von ihnen hängen viele andere Funktionseinheiten ab – aber die Domänendatenklassen selbst sind funktionsreich (d.h. kompliziert) und haben auch noch eine große Oberfläche durch ihre Properties.

Fazit: Je weiter bekannt eine Funktionseinheit, desto simpler sollte sie sein.

Ratschlag #5: Funktionseinheiten einzäunen

Funktionseinheiten sind nicht nur direkt, sondern auch indirekt gekoppelt. Änderungen an einer können sich deshalb u.U. weitreichend fortsetzen. Veränderungen an einem Teil führen dann zu Veränderungen an einem anderen, die wiederum zu Veränderungen an einem dritten Teil führen usw. Der Ausbreitungsprozess ist rekursiv entlang von Abhängigkeitsbäumen.

image

Um solchen Ausbreitungswellen Einhalt zu gebieten, ist es ratsam, Beziehungsnetzwerke in “Kästchen” zu isolieren. Selbst wenn die bisherigen Ratschläge beherzigt sind, sollten Beziehungen nicht beliebig verlaufen können.

image

Vielmehr sollte Kohäsives, d.h. Funktionseinheiten die enger in Beziehung stehen, von anderem deutlich getrennt werden. Das enger Zusammengehörige aus dem vorigen Bild ist im nächsten zu diesem Zweck “umzäunt” und die Beziehungen zwischen den “Nachbarn” verlaufen über eine “Kontaktperson”, einen Vermittler.

image

Änderungen hinter der Fassade des Vermittlers haben dann eine geringere Chance auf Abhängige durchzuschlagen.

image

Die sich durch die Trennung ergebenden Zusammenfassungen können dann als Funktionseinheiten auf höherer Abstraktionsebene angesehen werden. Auf sie sind dort dann natürlich dieselben Ratschläge zur Komplexitätsreduktion durch bewusste Beziehungspflege anzuwenden.

image

Fazit: Bewusst eingezogene Mauern zwischen Gruppen von Funktionseinheiten, die die Beziehungsaufnahme einschränken, reduzieren die Komplexität.

Zwischenstopp

Softwareentwicklung ist Beziehungsmanagement. Denn Software ist immer ein Geflecht aus Funktionseinheiten, die von einander abhängig sind. Fragt sich nur, wie. Ohne sorgfältige Planung von Beziehungen und der gegenseitigen Kompliziertheit der Bezogenen, läuft Software in eine Komplexitätsfalle.

Unabhängig von Plattform und Programmiersprache gibt es jedoch Ratschläge, denen Sie folgen können, um nicht Opfer wuchernder Beziehungen zu werden. Wo Beziehungen nötig sind, lässt sich ihr Beitrag zur Komplexität minimieren.

Bewegen Sie die Ratschläge #1 bis #5 im Herzen und schauen Sie dann auf Ihren Code oder die Empfehlungen der Literatur zu ihrer Plattform. Sie werden erkennen, dass hinter Technologiediskussionen und neuen Rezepten für bessere Software oft auch nur der Wunsch steht, Komplexität durch Beziehungsmanagement in den Griff zu bekommen. Exemplarisch seien im Folgenden einige bekannte Prinzipien aus diesem Blickwinkel betrachtet, die sich zum größten Teil auch als Bausteine bei der Clean Code Developer Initiative wiederfinden.

Beziehungsmäßige Prinzipien

Ratschlag #1 ist so fundamental, dass er im Grunde die Mutter aller Softwareprinzipien darstellt. Er findet sich im alten Prinzip Lose Kopplung wieder. Lose Kopplung bedeutet, Beziehungen zu vermeiden bzw. zu minimieren.

Ratschlag #2 ist immer wieder im Gespräch, wenn es um die Typisierung von Programmiersprachen geht. Typsicherheit, Typinferenz, dynamische Typen, Variants… das alles sind Aspekte expliziter syntaktischer Beziehungen.

Während lange Zeit galt, dass strenge Typsierung fundamental für die Reduktion der Fehlerzahl in Software sei, hat sich das Bild in den letzten Jahren jedoch verändert. Testautomatisierung wird von einigen Diskutanten als Alternative angeführt. Wo Tests automatisiert und schnell nach einem Compilerlauf ausgeführt werden können, ist Feedback zur Beziehungskonformität fast ohne Verzug auch zur Laufzeit zu erreichen. Das relativiert Ratschlag #2 etwas, dennoch gilt weiterhin: Einschränkungen der Beziehungsmöglichkeiten sind immer noch der Entdeckung von Beziehungsfehlern vorzuziehen. Gut, wenn Sie schnell erkennen können, wo etwas falsch läuft; noch besser, wenn Sie manche Fehler erst gar nicht machen können. Das Thema Abstrakter Datentyp (ADT) ist mit Testautomatisierung allemal nicht vom Tisch.

Womit wir bei den Prinzipien Information Hiding und Law of Demeter wären. Beide plädieren für eine Verringerung der Oberfläche von Funktionseinheiten, so dass weniger Beziehungen zu ihnen aufgebaut werden können. Auf höherer Abstraktionsebene tut das auch die Komponentenorientierung. Es sind Prinzipien und Praktiken im Sinne von Ratschlag #5.

Das gilt auch für das Single Responsibility Principle, das allerdings auch Ratschlag #3 und #4 zum Thema hat. Denn der Fokus auf nur eine Verantwortlichkeit pro Funktionseinheit reduziert deren Kompliziertheit. Dasselbe will natürlich auch Keep it simple, stupid (KISS).

Konkreter im Sinne von Ratschlag #3 ist dann Single Level of Abstraction (SLA), dessen Anwendung dazu führt, Kompliziertheit aus einer Funktionseinheit hinauswandert in neu geschaffene auf niedrigerem Abstraktionsniveau. SLA macht aus normalen Funktionseinheiten Süchtige - die allerdings simpler sind als vorher.

Das Open Close Principle (OCP) wendet sich auf der anderen Seite eher an promiskuitive Funktionseinheiten im Sinne von Ratschlag #4. Seine Anwendung macht sie weniger änderungsanfällig.

Das Inversion of Control Prinzip (IoC) schließlich adressiert abhängige Funktionseinheiten jeder Art. Es will ihre Beziehungen lockern durch Austausch statischer gegen dynamische Abhängigkeiten. Das reduziert zwar nicht die Zahl der Abhängigkeiten, macht sie aber flexibler, so dass Textautomatisierung besser ansetzen kann.

Fazit

Das Thema Beziehungsmanagement ist für die Softwareentwicklung also alt. Viele Prinzipien, die heute weithin anerkannt sind, um korrekten und evolvierbaren Code zu schreiben, drehen sich um Beziehungen – ohne das immer explizit zu machen. Damit ist die Zentralität des Beziehungsthemas leider leicht verschleiert. Statt eines Namens hat es viele. Das verhindert eine Diskussion darüber, wie es an der Wurzel gepackt werden kann.

Denn Prinzipien sagen zwar, was getan werden kann, um das fundamentale Beziehungsproblem zu mildern, doch sie drängen sich nicht auf. Jeder Entwickler hat die Freiheit, sie anzuwenden oder auch nicht. Im Zweifelsfall entscheidet er sich – natürlich nur für den Moment und immer aus guten Gründen – deshalb dagegen. Und so wächst die Komplexität in Software weiter, obwohl wir eigentlich genau wissen, was ihre Ursache ist und was wir dagegen tun können.

Prinzipien geben allerhöchstens Handreichungen, definieren aber keine Methode und kein Rahmenwerk. Prinzipien wie auch die hier gegebenen Ratschläge allein sind daher schwache Waffen gegen die Komplexität.

Ich glaube, wir müssen stärkere Waffen in Anschlag bringen. Wenn der Spruch gilt, “Sag mir, wie du mich misst, und ich sage dir, wie ich mich verhalten werde”, dann müssen wir mehr am Start haben als fluffige Prinzipien. Die sind nämlich keine genügend starken Wände, an denen man sich die Stirn einhauen kann, bis man von selbst auf dem Pfad der Tugend bleibt. Wenn Beziehungsmanagement so zentral für die Softwareentwicklung ist, dann müssen wir Sprachen oder allemal Denkwerkzeuge haben, die das Beziehungsmanagement in den Mittelpunkt stellen. Beziehungen müssen sich weitgehend von allein und intuitiv den Ratschlägen entsprechend bilden. Sonst ist nicht zu hoffen, dass die wenigen Ratschläge oder die vielen Prinzipien zur Reduktion von Komplexität führen.

Mittwoch, 16. Februar 2011

Traktor für die Softwareentwicklung

Wer ist eigentlich am wichtigsten in einem Softwareentwicklungsprojekt? Sind es die Entwickler, ohne die es zu keiner Software kommt? Oder ist es der Requirements Engineer, ohne den die Entwickler nicht wüssten, was sie tun sollen? Oder ist es der Verkäufer, ohne den es gar kein Projekt gäbe, für das Anforderungen erhoben werden müssen? Oder ist es der Kunde, der schließlich das Geld gibt? Oder ist es – sofern vorhanden – die Qualitätssicherung, ohne die man das Entwicklungsprodukt nicht auf den Anwender loslassen kann?

Natürlich sind all diese Rollen wichtig. Erst wenn alle zusammenspielen, kommt gute Software heraus. Und dennoch… ich denke, es gibt eine erste Rolle und den vielen gleichen, den primus inter pares. Ohne sie geht nichts. Und ohne ein größeres Bewusstsein ihr gegenüber wird es nicht besser mit der Softwareentwicklung. Merkwürdigerweise hat diese Rolle keinen eigenen Namen, glaube ich. Ich kenne zumindest keinen.

Diese Rolle, die ich meine, die wichtigste, das ist… die Abnahme.

Der Abnehmer, d.h. der, der prüft, ob eine Software den Anforderungen entspricht, ist die wichtigste in der Produktionskette vom Kunden über den Verkauf bis zum Anwender.

Scrum betraut den ProduktOwner (PO) mit der Verantwortung. Da ist die Abnahme Teil des Prozesses. Mich stört dabei allerdings die Personalunion von Anforderer und Abnehmer. Der PO schiebt vorne Anforderungen rein und nimmt hinten ihre Erfüllung ab. Klingt plausibel, denn weiß er als Anforderer nicht am besten, ob seine Wünsche erfüllt sind? Angesichts der Wichtigkeit der Abnahme finde ich die Vereinigung jedoch kontraproduktiv.

Und warum ist die Abnahme so wichtig? Weil an ihr das liebe Geld hängt. Erst wenn Software abgenommen ist, fließt das Geld vom Kunden zur Softwareentwicklung.

Alles, was im Backlog steht, alles, was das Team so mühevoll produziert, alles, was die Qualitätssicherung durchwinkt, all das ist eitler Tand, solange er nicht abgenommen ist. Backlog, entwickelte Features und qualitätsgesicherter Code inkl. aller Dokumentation ist Umlaufbestand und damit gebundenes Kapital.

Wenn ein Team von 5 Entwicklern einen Sprint von 14 Tagen hinlegt, dann ist dieser Umlaufbestand am Ende des Sprints sehr hoch. Sein Wert beträgt nach landläufiger Rechnung mindestens 5 * 10 Arbeitstage * Deckungskosten/Entwickler, also z.B. 50 * 300 EUR = 15.000 EUR. Wenn dann der PO nicht zügig die produzierten Features abnimmt, ist das ein Problem. Dann fließt kein Geld. Das heißt, das Softwareentwicklungsunternehmen tritt in Vorleistung und kann ein Liquiditätsproblem bekommen.

Soweit aber nur die landläufige Rechnung in einer idealen Welt, in der der PO quasi blitzartig abnimmt.

Jetzt ein Blick durch die Brille der Durchsatzrechnung (throughput accounting) der Theory of Constraints (TOC). Dazu hänge ich mal die Rollen zu einer Produktionskette zusammen:

Backlog füllen (Bf) –> Features implementieren (Fi) –> Qualität sichern (Qs) –> Features abnehmen (Fa)

Jeder Produktionsschritt kann theoretisch 8 Std pro Tag leisten. Allerdings haben sie unterschiedliche Kapazität und stehen in der Praxis eben nicht 8 Std pro Tag zur Verfügung.

Die Leistungsfähigkeit der Schritte misst sich am Backlog. Bf ist also das Maß aller Dinge. Wenn Bf 8 Std das Backlog füllt, wie lange braucht dann Fi, um diese Menge an Features umzusetzen? Wie lange braucht Qs, um die Qualität zu prüfen? Wie lange braucht Fa, um die Features abzunehmen?

Ganz grob sehen die Verhältnisse vielleicht so aus:

Bf:Fi = 1:10 bis 1:50 – Fi braucht 10 bis 50 Mal so lange wie Bf für eine Menge an Features.

Bf:Qs = 1:0,1 bis 1:5 – Die Qualitätssicherung kann manchmal sehr schnell sein, manchmal braucht sie allerdings auch einige Zeit; in jedem Fall sollte Qs aber schneller als die Entwicklung sein.

Bf:Fa = 1:0,1 bis 1:2 – Die Abnahme sollte nicht so lange dauern wie die Qualitätssicherung; aber das ist sicherlich von der Güte der Anforderungen abhängig. Je besser die Definition of Done, je umfangreicher die Akzeptanztests, desto schneller die Abnahme.

Die langsamste Produktionseinheit ist Fi; sie ist der Engpass der Produktion. So scheint es und deshalb wird dort auch am meisten herumgeschraubt, um die Effizienz zu erhöhen.

Meiner Erfahrung nach geschieht das durchaus zurecht. Die Effizienz der Softwareentwicklung hat, um es vorsichtig auszudrücken, oft noch einiges ungehobenes Potenzial. Im Sinne der TOC lohnt sich die Effizienzsteigerung dort jedoch nicht, solange sie nicht wirklich der Engpass ist. Und das scheint mir der Fall in vielen Projekten.

Oberflächlich betrachtet, scheint die Fi immer der Engpass – aber nur solange die Produktionskette unvollständig ist und ohne die Abnahme gesehen wird.

Agile Softwareentwicklung kennt eine Definition of Done. Die empfinde ich oft jedoch noch als sehr technisch. Ihr fehlt der Reflex, den Abnehmer einzubeziehen. Alle Akzeptanztests sind eitel, solange nicht ein vom Kunden autorisierter Abnehmer sie durchführt und abzeichnet. Denn erst dann kann Geld fließen.

imageWo ist aber dieser Abnehmer? In vielen, nein, in den meisten Projekten, die ich als Berater im Rahmen meiner Arbeit als clean-code-advisor sehe, gibt es einen solchen Abnehmer nicht. Es gibt erstens niemanden, der verlässlich abnimmt, und zweitens sogar einen eigenen Willen hat, abzunehmen und deshalb immer wieder an den Kettengliedern vor sich zieht.

Die allermeisten Projekte, die ich sehe, haben keinen Traktor. Das heißt, auch wenn die Kapazität von Fa im Vergleich zu Fi oder Bf gering sein muss, gibt es sie im Grunde nicht.

Nehmen wir an, Bf hat 3 Tage lang am Anfang des Monats das Backlog gefüllt; nehmen wir an, Fi und Qs haben es geschafft, einen Großteil dieser Füllung bis Monatsende abzuarbeiten. 30.000+ EUR liegen damit auf Halde. Sie können erst nach offizieller Abnahme abgerechnet werden. Aber wo ist der Abnehmer?

Der bräuchte nach obigen Kapazitätsverhältnissen ja vielleicht nur 1-2 Tage, um die Halde durchzuarbeiten. Doch diesen Abnehmer gibt es in den meisten Fällen nicht. Ja, genau, es gibt niemanden, der konsequent jeden Monat 1,2 oder 3 Tage nur dafür zuständig ist, gelieferte Features abzunehmen. Geschweige denn, dass es jemanden gäbe, der das nicht nur alle Monate tut, damit gar nicht so eine große Halde entsteht, sondern jede Woche oder gar jeden Tag.

In den meisten Projekten ist also gegen die Intuition nicht Fi oder Qs der Engpass, sondern Fa. Fa steht nicht verlässlich mit vielleicht 1 Tag pro Woche (oder 4 Tagen pro Monat) zur Verfügung, sondern vielleicht nur mit 1 Tag pro Monat. Fa fällt damit sozusagen 3 von 4 Tagen, die nötig wären, aus.

Jetzt die Frage, wie teuer ist dieser Ausfall, dieser Mangel an Kapazität. Der Kunde mag rechnen, dass der Abnehmer pro Tag vielleicht 500 EUR kostet. Deshalb will er ihn auch nur so wenig wie möglich bei der lästigen Abnahme einsetzen, die ja irgendwie nichts bringt. Er spart also 1.500 EUR, wenn er ihn nur 1 Tag/Monat statt 4 Tage dem Projekt zuteilt.

Aus Sicht der Softwareentwicklung sieht das Bild jedoch anders aus. Denn wenn der Abnehmer nur 1 Tag statt 4 verfügbar ist, d.h. auch nur 25% der Halde abnehmen kann, dann wird die Halde immer größer. Am Ende des ersten Monats liegen 30.000+ EUR auf Halde, der Abnehmer prüft im Rahmen seines Tages 25% und es werden 7.500 EUR zur Bezahlung freigegeben.

Am Ende des zweiten Monats liegen dann schon 52.250+ EUR auf Halde. Davon werden wieder nur 7.500 EUR bezahlt, weil der Abnehmer nur einen Tag Zeit hat. Und so weiter, und so fort.

Natürlich ist das eine vereinfachende Rechnung – aber vielleicht gar nicht so stark, wie es scheint. Denn was an Abnahmefrequenz und –kapazität in manchen Projekten mehr vorhanden ist, wird durch Abnahmeverweigerungen womöglich wett gemacht. Abnahmen laufen ja nicht problemlos einfach durch, sondern führen sofort oder im späteren Betrieb auch zu Abweisungen.

Ich bleibe deshalb dabei: Der erste Engpass der meisten Projekte ist die Abnahme. Sie ist nicht verlässlich und mit zu wenig Kapazität ausgestattet. Und vor allem, sie wird ihrer Rolle nicht gerecht, weil sie nicht wirklich an der Kette vor sich zieht. Sie steht nicht jeden Tag auf der Matte und fragt, “Wo sind die nächsten Produkte, die ich abnehmen kann?”

Und das hat Auswirkungen. Weitreichende. Die erste und für Manager eigentlich unmittelbar spürbare: die Liquidität ist geringer als sie sein könnte. Immer wieder tritt die Entwicklung in Vorleistung, weil sie auf Halde produziert hat. Da ist man stolz, dass die Anforderungen munter ins Backlog fließen; man freut sich an einer schnurrenden Implementierung und einer sauberen Qualitätskontrolle – nur am Ende wird nicht abgenommen. Die ganze schöne hohe Qualität liegt dann auf Halde. Und letztlich weiß keiner, ob die hohe Qualität überhaupt den Kundenwunsch trifft. Denn das stellt sich erst heraus, wenn sie auch abgenommen wurde.

Die zweite, eher subtile Auswirkung: Solange der Engpass Abnahme nicht beseitigt ist, lohnen alle anderen Verbesserungsmaßnahmen an Produktionsschritten davor nicht. Das ist eine ganz klare Aussage der TOC.

Es lohnt nicht, den Verkauf anzukurbeln, wenn hinten keine Abnahme stattfindet. Insofern könnte es sogar eine Maßnahme sein, den Verkauf in die Abnahme einzubeziehen. Vielleicht sollte er seine Provision erst bekommen, wenn auch abgenommen ist?

Es lohnt nicht, Aufwand in Continuous Integration zu stecken, solange es keine “Continuous Acceptance” gibt. Dasselbe gilt für höhere Effizienz bei der Implementation oder mehr Kapazität in der Qualitätssicherung. Das alles führt nur zu einer schnelleren Füllung der Halde. Produktionskosten werden schneller in Bestände überführt; der Durchsatz steigt deshalb aber nicht.

Deshalb lautet in letzter Zeit meine erste Frage an Projektverantwortliche, wenn sie um Rat suchen: Wer ist für die Abnahme zuständig, wie aktiv zieht derjenige am Team, wie hoch ist die Abnahmefrequenz?

Bevor nicht diese Verantwortlichkeit ganz klar zugewiesen ist, bevor nicht die diese Rolle von sich aus und ganz verlässlich immer wieder bei Fi und Qs auf der Matte steht, bevor nicht mindestens jede Woche, besser noch jeden Tag!, abgenommen wird, solange lohnt kaum eine andere Maßnahme.

Das mag bitter klingen, weil Softwareteams, die sich an Problemen aller Art abarbeiten, auf die Abnahme wenig Einfluss haben, aber es hilft nichts. So ist die Natur der Dinge. Und die Praxis Prozessoptimierung in vielen Branchen und zahllosen Unternehmen zeigt, dass es nur so geht: Verbesserung muss am Engpass ansetzen, sonst verpufft sie und/oder führt zu Frust. Der Engpass in der Softwareentwicklung, deren Zweck das Geld verdienen ist, ist nun aber zunächst derjenige, der das Geld freigibt.

Bringen Sie also die Abnahme als Traktor für den ganzen Entwicklungsprozess in Stellung, dann wird alles andere leichter oder gar erst möglich.

Sonntag, 6. Februar 2011

Spielend programmieren

imageBesser wird es nicht durch Klagen. Besser wird es nur, wenn man sich überhaupt vorstellen kann, wie es besser sein könnte. Dafür muss man sich manchmal frei machen von dem, was ist. Einfach alle Begrenzungen hinter sich lassen. Mal frei fabulieren, wie die Welt aussehen sollte, und beherzt eine Antwort finden auf die Frage: “Ja, wie hätte ich es denn gern, wenn ich mir etwas wünschen dürfte von einer Fee?”

Heute habe ich mir gegönnt, diese Frage für die Programmierung mal für mich zu beantworten. Geplant hatte ich das nicht. Eher bin ich ohne zu fragen über meine Antwort gestolpert.

Wie wünsche ich mir also die Programmierung?

Ich wünsche mir die Programmierung spielerisch(er). Ich wünsche mir, dass Programmieren so funktioniert wie TinkerBox von Autodesk.

TinkerBox ist ein Spiel, in dem man “Maschinen” baut bzw. vervollständigt, um eine Aufgabe zu lösen. Zugegeben, das sind sehr, sehr, sehr einfache Aufgaben im Vergleich zu einer Warenwirtschaft oder einem Compiler oder einer Stellwerkssteuerung.

Aber auch Druckpressen, Autos, Fahrstühle sind sehr viel komplizierter als die TinkerBox-Maschinen und doch funktionieren sie letztlich nach denselben Gesetzen.

Hier ein paar Impressionen von TinkerBox:

Als ich mit TinkerBox angefangen habe auf meinem iPad zu spielen, habe ich einfach das Gefühl gehabt: “Wow, so sollte auch die Programmierung laufen!” Ich möchte Programme visuell zusammensetzen. Ich möchte sie sofort probeweise laufen lassen. Dabei möchte ich zusehen, wie die Teile zusammenspielen.

Natürlich kann das nicht ganz so simpel sein wie bei TinkerBox. Aber warum muss es denn sooooo anders aussehen? Warum muss es aussehen wie heute, wo ich eigentlich nur wie vor 30 Jahren Text in einer imperativen Sprache in einen Editor klopfe? Das kann doch nicht das Ende der Fahnenstange sein. Wir können doch nicht ernsthaft der Meinung sein, mit einer textuellen IDE (und ein bisschen Visualisierung drumherum) die Spitze des Möglichen in der Softwareentwicklung erklommen zu haben.

Nein, ich möchte, dass das anders aussieht. Ich will Software aus Bausteinen aufbauen. Ich will sie zusammenstecken. Ich will sehen, wie sie funktioniert – zum einen, indem ich am Code die Funktionalität ablese, zum anderen, indem ich den Code dabei beobachte.

Manche der Bausteine sind dabei Standardbausteine, andere sind Bausteine, die ich noch “zuhauen” muss, wieder andere setze ich aus anderen zusammen.

image

Bei TinkerBox gibt es nur Standardbausteine. Die Kunst besteht darin, sie in zielführender Weise zu kombinieren. Für die Softwareentwicklung reicht das natürlich nicht. Wir brauchen Bausteine, die wir “parametrisieren” können. In die gießen wir mehr oder weniger normalen Quellcode. Das finde ich ok. Denn die Menge des Quellcodes ist dann überschaubar.

TinkerBox hat in mir den Gedanken verstärkt, dass wir in der Softwareentwicklung Konstruktion und “Kreation” strikt trennen müssen. Wir brauchen beides, aber es sind grundsätzlich verschiedene Tätigkeiten.

Bei der Konstruktion nehme ich Bausteine und setze aus ihnen etwas Größeres zusammen.

Bei der Kreation denke ich mir neue Bausteine aus (oder “parametrisiere” Standardbausteine). (Ob “Kreation” der beste Begriff dafür ist, lasse ich mal dahingestellt. An dieser Stelle wollte ich aber nicht Implementation schreiben.)

Bausteine zusammenstecken, sie zu einem funktionierenden Ganzen fügen, das ist etwas ganz anderes, als Bausteine zu entwickeln, zu kreieren.

In der Softwareentwicklung trennen wir aber bisher nicht sauber, sondern sind im Grunde ständig mit der Kreation beschäftigt. Die jedoch ist viel schwieriger als die Konstruktion. Der einfache “Beweis”: Selbst Kinder können mit Legobausteinen tollste Dinge konstruieren – kreiert haben aber nicht Kinder die Legobausteine, sondern Erwachsene.

Genauso ist es mit Excel. Millionen von Poweruser können aus den Standardbausteinen in Excel tollste “Rechengebilde” konstruieren. Kreiert haben diese Standardbausteine jedoch Programmierer.

So ganz neu ist die Vorstellung, die ich hier äußere, natürlich nicht. Von Komponenten, die per glue code nur noch verbunden werden müssen, träumte man schon in der 1990ern oder gar davor. Realisiert ist diese Vision aber nur sehr begrenzt.

Mir geht es auch nicht darum, Laien zu Softwareentwicklern zu machen. Ich möchte den Softwareentwicklern nur das Leben erleichtern. Sie sollen sich mehr auf das Wesentliche konzentrieren. Das – so stelle ich mir in einem kühnen Traum vor – können sie aber besser, wenn Programme visueller machen. Die Anhaftung an Text als primärem Ausdrucksmittel für Software, ist anachronistisch und kontraproduktiv. Ich kenne keine andere Branche, in der Designdokumente primär textueller Art sind; nur die Softwareentwicklung beharrt darauf. Denn Programmierung ist Design.

Also: Befreien wir uns von der Last der Texte! Machen wir die Softwareentwicklung haptischer. Entlasten wir uns durch Trennung von Konstruktion und Kreation. Ich glaube, dann wird vieles besser in der Programmierung. Denn dann können wir besser über Software reden und wir können dann besser gemeinsam an Software arbeiten.

Dienstag, 1. Februar 2011

Mehr vom Selben hilft nicht mehr

Martin Fowlers Vortrag hat mich leider enttäuscht. Heute abend hätte er über “Software Design in the 21st Century” sprechen sollen. Gesprochen hat er aber stattdessen in drei “Minivorträgen” über DSLs – anlässlich seines gerade erschienenen Buches zum Thema –, REST und die Agilitätsbewegung am Ende der ersten 10 Jahre. Das war alles wenig inspirierend und wenig inspiriert, fand ich.

Nun frage ich mich aber, warum hat er nicht das ursprüngliche Thema behandelt? Sicher war nicht nur ich im ausverkauften Saal auf seine Sichtweise gespannt. Meine Vermutung – mal so ganz keck geäußert: Er hat dazu auch keine rechte Idee.

Die Branche mokelt halt so vor sich hin. SOA ist entweder tot oder hat sich still ins Pragmatische zurückgezogen. Funktionale Programmierung rettet im Verbund mit Multi-Core Prozessoren auch nicht kurzfristig die Softwarewelt. Um XP ist es sehr still geworden; funktioniert also entweder einfach oder wird so lange adaptiert, bis davon nichts mehr übrig ist. Scrum verliert sich in Lagerfehden. Und sonst so?

Tja… das große Problem wartbarer Software, d.h. flexibler, evolvierbarer, langlebiger und auch noch möglichst fehlerfreier Software, die im Rahmen gegebener Ressourcen entwickelt wird, das Problem ist schlicht ungelöst. Und eine Lösung ist nicht in Sicht.

Nicht, dass ich eine Silberkugel im Samtkästchen von Martin Fowler erwartet hätte. Aber mit DSLs, REST und ein bisschen Geschichten erzählen unter dem Titel “Software Design in the 21st Century" aufzutreten, finde ich schwach.

So einfach vergessen kann ich den Abend aber nicht. Denn da ist etwas, das Martin Fowler eher nur am Rande gesagt hat, das mich wurmt. Er hat nämlich gesagt, wir stünden bei der Agilität noch am Anfang. Warum? Weil wir selbst bei der Objektorientierung nach 40 Jahren noch nicht am Ziel seien. Die Agilität solle sich also besser darauf einstellen, auch noch ein paar Jahre zu brauchen.

Einerseits ist da sicherlich wahr: Entwicklungen brauchen ihre Zeit, selbst wenn sie in die richtige Richtung gehen. Und da die Agilität nicht nur mit Technologie zu tun hat, sind ihre Auswirkungen weitreichender als bei der Objektorientierung. Mehr Menschen müssen sich anpassen. Also dauert es auch lange oder gar länger.

Aber andererseits… Wir reden hier ja nicht über Naturgesetze oder den menschlichen Körper. Da mag es Jahrhunderte dauern, bis wir die wahren Zusammenhänge erkennen. Wir reden über menschengemachte Konzepte und Werkzeuge. Und da erlaube ich mir die Frage: Wenn etwas Menschengemachtes nach Jahrzehnten immer noch Schwierigkeiten bereitet, ist das dann ein Problem der Leute, die damit immer noch und immer wieder Schwierigkeiten haben? Oder ist das ein Problem von Konzept bzw. Werkzeug?

Die Natur lässt sich nicht ändern, ob wir sie verstehen oder nicht. Bei der Softwareentwicklung habe ich da aber einen anderen Anspruch.

Fowler, Beck, Martin et al. werden nicht müde hervorzuheben, dass die allermeisten Entwickler die Objektorientierung nicht wirklich verstanden hätten. Es würde eher mit OO-Sprachen immer noch prozedural programmiert.

Das mag sein – aber wessen Problem ist das denn? Warum erhebt sich nicht laut und breit die Frage, ob denn Objektorientierung in der scheinbar nur sehr, sehr, sehr schwer zu vermittelnden “richtigen” Weise überhaupt erstrebenswert ist? Wenn Millionen von Entwicklern über Jahrzehnte es nicht hinkriegen, dann können wir – so glaube ich – ausschließen, dass die Millionen zu doof sind. Oder wenn, dann ist das eben so. Auch egal. In jedem Fall muss dann die Methode, das Paradigma überprüft werden.

In der Finanzwelt gilt: “Nicht realisierte Gewinne sind keine Gewinne.” Das sollten wir auf die Softwareentwicklung übertragen: “Erfolgversprechende Ansätze, die jahrzehntelang hinter den Erwartungen zurückbleiben, sind überbewertet.”

Wenn die Objektorientierung nach 40 Jahren die Softwareentwicklung nicht deutlich herausgerissen hat aus der Unzuverlässigkeit, dann ist sie schlicht das falsche Mittel. Dann müssen wir ein anderes suchen.

Und genauso die Agilität: Wenn die nach 10 Jahren noch am Anfang steht bei dem großen Hype, den sie erfahren hat, dann müssen wir uns etwas anderes suchen, um die Probleme, die immer noch so weit verbreitet sind, zu lösen. Dann ist irgendetwas noch mindestens knapp daneben bei der Agilität.

Was mich also enttäuscht hat bei Martin Fowlers Vortrag war die ruhige “Sturheit”, mit der er “mehr vom Selben” wenn schon nicht gefordert, dann doch aber suggeriert hat. Mehr “richtige” Objektorientierung hilft, mehr “richtige” Agilität hilft. “Leute, haut einfach rein; ihr habt es noch nicht doll genug versucht.” Klingt wie aus einem Watzlawik-Buch. Nur da wird solches Verhalten leider als pathologisch entlarvt.

Das Ergebnis des Abends für mich: Martin Fowler hat für mich live repräsentiert, was ich nach Lektüre seines und anderer Blogs schon geahnt hatte. Die Vordenker von einst sind auch nur Menschen und können nicht dauerhaft vordenken. Auch sie sind Gefangene eines Systems, selbst wenn sie es selbst (mit)geschaffen haben. Oder gerade deshalb?

Wirklich Vordenkerisches werden wir kaum noch von Fowler oder Martin oder Beck zu hören bekommen. Alistair Cockburn scheint mir allerdings noch etwas wacher. Ansonsten schauen wir besser an den Rand des Mainstream. Dort steigen die Nebel dessen auf, das sich dermaleinst zum Software Design des 21. Jahrhunderts verfestigen wird. NoSql, Erlang, DCI, Kanban… das sind für mich Begriffe, die Impulse in die Branche senden.

Keine Ahnung, wie das aber alles mal am Ende aussehen wird. OO und Agilität und DSLs und REST werden dadurch natürlich auch nicht von der Bildfläche spurlos verschwinden, sondern vielmehr assimiliert. Aber die Softwarentwicklungswelt wird anders aussehen, da bin ich mir sicher. Denn wenn sie nicht anders aussieht, dann habe ich wenig Hoffnung, dass wir uns aus dem wachsenden Brownfield befreien.