Follow my new blog

Samstag, 28. Juni 2014

Regelmäßiges Lernen - Mein Commitment

imageGute Softwareentwicklung gibt es nicht ohne regelmäßiges Lernen. Technologisch bleibt man sonst immer weiter zurück. Aber auch neue Methoden können sonst nicht wirklich eingeführt werden.

TDD, Clean Code, NoSql, Reactive Programming, F# oder was sich sonst noch nützlich auf die Softwareproduktion auswirken könnte, kann man nicht im Kopf anschalten. Dafür braucht es vielmehr Zeit, um zu lesen, zu denken, zu üben, bevor man es in den Alltag der Produktionscodeentwicklung übernehmen kann.

Ich glaube, 20% Lernzeit während (!) der Arbeitszeit wären gut. Mehr als 10% scheinen in den allermeisten Unternehmen jedoch nicht machbar. Dann also 10% - aber nicht weniger!

Diese 10% können aus autodidaktischem Lernen für sich, Lernen in der Gruppe und Teilnahme an Kursen bestehen. Einer meiner Kunden stellt seinen Entwicklern z.B. 150 Stunden pro Jahr für das Lernen zur Verfügung. Das sind ca. 10% der Arbeitszeit, wenn ich mal 200 Arbeitstage à 8 Stunden rechne. Allerdings verplant man diese 150 Stunden in Form von Seminarteilnahmen. Das ist gut gemeint - aber kontraproduktiv. Denn wann soll denn das, was man im Seminar lernt, geübt werden? Seminarteilnehmer können ja nicht nach 2–3 Tagen Seminar, was als Lernstoff vermittelt wurde. Dafür ist unser Metier zu kompliziert. Unser Lernstoff muss geübt werden. Und nochmal geübt werden. Bevor man ihn halbwegs sicher auf Produktionscode anwenden kann.

Kein Musiker probt auf der Bühne. Kein Chirurg lernt neue Techniken am gewöhnlichen Krankenhauspatienten. So sollte es auch bei der Softwareentwicklung sein. Es braucht für Neues geschützte Übungszeit. Regelmäßig.

Das fällt schwer. Das höre ich immer wieder. Es passt einfach nicht zum vorherrschenden Mindset der 110% Auslastung aller Mitarbeiter mit Tagesgeschäft. Doch es hilft nichts. Wer nachhaltig “wirtschaften” will, der muss dafür Zeit zur Verfügung stellen. Und nicht nur fürs Lernen.

Nun mag man sagen, “Ralf, du hast gut reden. Als Berater/Trainer ist dein Leben viel einfacher. Du kannst dir Zeit nehmen, wie du willst.”

Aber das stimmt nicht. Auch ich habe ja mein Tagesgeschäft. Klar, das sieht anders aus als das eines Entwicklungsteams. Von den Prioritäten her fühle ich mich da allerdings genauso eingeengt. Für einen Entwickler mag es aussehen, als würde ich ständig lernen. Ist nicht ein Buch über Softwareentwicklung lesen, einen Artikel schreiben, ein Seminar vorbereiten Lernen?

Das stimmt in Bezug auf die Softwareentwicklung. Es stimmt aber nicht in Bezug auf die Vermittlung von Softwareentwicklung. Was für Entwickler Lernen ist, ist für mich Tagesgeschäft.

Das verwechsle ich allerdings auch oft. Deshalb muss ich einsehen, dass selbst ich, nicht genug lerne. Ich lebe sozusagen selbst noch nicht das, was ich predige. Das ist Mist. So kann ich meine Überzeugung nicht authentisch vermitteln. I need to put my money where my mouth is :-)

Das Lernversprechen

imageDas will ich, nein, das muss ich nun ändern. Da geht kein Weg dran vorbei, wenn ich als One Man Think Tank weiterhin glaubwürdig sein will. Also lege ich hier ein Versprechen ab.

Ich verspreche, dass ich fortan mindestens 10% meiner Arbeitszeit[1] dem Lernen widmen werde.

Dieses Lernen kann im Besuch von Vorträgen oder Seminaren bestehen, das kann aber auch Lesen und Üben daheim sein.

Da mein Job die Vermittlung von Softwareentwicklungsmethodiken ist, können Lerninhalte natürlich nicht Softwareentwicklungsmethodien sein. Mein Lernen muss vielmehr auf der Meta-Ebene bzw. im Allgemeineren stattfinden.

Oder ich könnte auch sagen: Ich muss beim Lernen dasselbe Gefühl haben, wie meine Kunden. Einerseits muss ich es wollen, andererseits muss es mir aber auch wehtun. Auch ich muss beim Lernen spüren, dass ich eigentlich etwas anderes dringender tun müsste.

Aktivitäten auf die das für mich zutrifft, sind z.B. eine Sprache lernen, Meditation, die Beschäftigung mit “Sachthemen”.

Für Sie mögen das Freizeitaktivitäten sein, für mich gehört das jedoch im weiteren Sinn zum Job:

  • Mein Job ist es, mit Sprache umzugehen, sowohl mit natürlicher wie mit formaler. Auf die eine oder andere Weise. Deshalb ist es wichtig, dass ich meinen “Sprachmuskel” fit halte. Jede Sprache, die ich lerne oder deren Kenntnis ich verfeinere, macht es mir leichter, zu kommunizieren, was ich vermitteln will.
  • Mein Job ist es, mich vielen Einflüssen auszusetzen und oft zu reisen. Innere Ruhe und Gelassenheit sind dann wichtig, um in dem Rahmen eine balancierte und authentische Botschaft zu vermitteln. Medidation und die Beschäftigung mit spirituellen Themen hilft, diesen Zustand herzustellen.
  • Die Softwareentwicklung profitiert davon, sich durch andere Disziplinen oder sogar ganz allgemein “durch das Leben” informieren zu lassen. Bauarchitektur, Biologie, Physik, Soziologie usw. halten viel bereit, von dem wir lernen können. Darüber hinaus kann meine didaktische und methodische Praxis als Trainer nur davon profitieren, wenn ich mich in anderen Bereichen umschaue (z.B. systemische Analyse, Lernpsychologie). Ich muss also “Sachthemen” erkunden - und das durchaus, ohne immer genau zu wissen, wann ich welches wie anwenden kann. Sozusagen Grundlagenforschung statt angewandte Forschung.

Natürlich habe ich in der Vergangenheit diese Aktivitäten schon betrieben - nur nicht systematisch. Da war es Freizeit und ich konnte jederzeit damit aufhören. Nun will ich das ändern. Darin besteht mein Commitment.

Ich verspreche, jede Woche mindestens 4 Stunden wie folgt ins Lernen und Üben zu investieren:

  1. Sprachenlernen: Ich widme jeden Arbeitstag 20–30 Minuten dem Lernen einer Sprache. Derzeit ist das Französisch. 5x20=100 Minuten, d.h. 1,66 Stunden.
  2. Meditieren: Ich mediere jeden Tag 10–15 Minuten. 7x10=70 Minuten, d.h. 1,16 Stunden.
  3. Sachthemen: Ich lese jeden Arbeitstag 15–30 Minuten über ein Sachthema. 5x15=75 Minuten, d.h. 1,25 Stunden. Dazu mögen über das Jahr verteilt Konferenzen oder Seminare kommen, wo ich mich en bloc länger mit einem Thema befasse. In diesem Jahr waren das z.B. schon eine Konferenz zum Thema Unternehmertum und ein Seminar über Prozessmanagement.

Das sind Aktivitäten, von denen ich weiß, dass sie für mich wichtig sind. Ich muss hier regelmäßig am Ball bleiben. Dennoch fällt es mir schwer, sie auch ständig während des Tagesgeschäftes auf dem Zettel zu haben.

Jeden (Arbeits)Tag 15–30 Minuten mit so einer Aktivität verbringen, führt das denn zu Fortschritt? Ja, das glaube ich ganz sicher. Nicht kurzfristig, aber mittel- und langfristig. Es gibt ja auch keine Rüstzeiten und spezieller “mental state” muss auch nicht aufrechterhalten werden. Außerdem steht es mir jederzeit frei, mehr Zeit einer dieser Aktivitäten einzuräumen.

Soviel zu meinem Versprechen. Und wie dokumentiere ich, dass ich es einhalte?

Ich benutze die App Lift, um die Durchführung zu protokollieren.

imageimage

imageimage

Nach Abschluss einer Aktivität hake ich sie in der App ab. Das an sich ist schon ein kleiner Erfolg. Noch motivierender ist es jedoch, in der Übersicht zu sehen, wie kontinuierlich ist dabeigeblieben bin.

Wer mag, kann meinen Fortschritt verfolgen. Ich habe die lift-Aktivitäten öffentlich gemacht:

Spätestens in 3 Monaten berichte ich dann hier, wie es mir mit der Erfüllung meines eigenen Anspruchs ergangen ist. (Dass ich unterwegs nicht mogle, müssen Sie mir glauben. Ich mache also nur einen lift-Haken, wenn ich wirklich eine Aktivität durchgeführt habe.)

Und nun: Ich bin genauso gespannt wie Sie :-)

Let the learning begin…


  1. Meine Arbeitszeit ist nicht so klar abgezirkelt wie die eines Angestellten. Als Freiberufler bin ich sehr frei, was Zeit und Datuer meiner Arbeit angeht. Außerdem verschwimmen Arbeit und Freizeit bei mir. Mein Versprechen bezieht sich der Einfachheit halber auf eine 40 Stunden Woche. Ich verspreche also 4 Stunden Lernen pro Woche.

Montag, 9. Juni 2014

Die wichtigste Rolle in der Softwareentwicklung

Natürlich ist Softwareentwicklung eine Tätigkeit, an der viele beteiligt sind. Alle Rollen sind wichtig.

Und doch… ich glaube, es gibt eine Rolle, die ist wichtiger als andere. Sozusagen erste unter gleichen. Das ist die Rolle des Product Owner (PO).

Nicht die Programmierer sind die Wichtigsten, nicht die Qualitätssicherer, sondern der PO. Das stürzt hoffentlich niemanden in Ärger oder Verzweiflung ;-)

Wichtiger ist der PO, weil er die Softwareentwicklung klammert. Er hat zwei Hüte auf. Er kippt einerseits in den Produktionsprozess vorne Anforderungen als Input hinein. Andererseits nimmt er am Ende den Output entgegen und gibt dazu Feedback. Er ist Sender und Empfänger, Quelle und Senke.

Damit ist er für den Kurs der Softwareentwicklung verantwortlich. Er ist wie ein Kapitän. Der Steuermann mag lenken, der Ausguck mag vorausschauen, der Maschinist mag für Vortrieb sorgen; und alle entscheiden dabei situativ.

Doch allein der Kapitän entscheidet strategisch, d.h. im Hinblick auf das zu erreichende Ziel. Er gibt den Rahmen für alle anderen vor.

Genauso der PO. Er hat als Ziel den Nutzen für den Anwender im Auge. Dafür soll Software mit bestimmten Eigenschaften bereitgestellt werden. Wie, das entscheidet der PO. Er ist Stellvertreter des Kunden auf dem Projektschiff, so wie ein Kapitän Stellvertreter des Reeders auf einem Handelsschiff ist.

Selbst der Softwarearchitekt ist dem PO untergeordnet. Architektonische Entscheidungen müssen sich immer im Rahmen dessen bewegen, was der PO an Anforderungen stellt.

Die wichtigste Aufgabe des Product Owner

Besonders schwierig macht es die Aufgabe des PO, dass er als Klammer sowohl am Anfang wie am Ende des Produktionsprozesses steht.

Deshalb möchte ich betonen, was die wichtigste Aufgabe des PO ist. Welchen Hut sollte er vor allem aufhaben?

Wikipedia widmet dem PO 14 Sätze. Davon ist allerdings nur ein Teilsatz der aus meiner Sicht wichtigsten Aufgabe des PO gewidmet: Er trifft “die Entscheidung darüber, ob die vom Entwicklungsteam am Ende jedes Sprints gelieferte Funktionalität akzeptabel ist.”

Das vor allem muss der PO tun: akzeptieren. Er muss Lieferungen von Programmierung und QS prüfen und dazu Feedback geben. Im besten Fall akzeptiert er das Gelieferte, im schlechteren Fall verweigert er die Weitergabe und muss Nachbesserungen anweisen.

Am wichtigsten ist der PO also in seiner Funktion als Senke, als schließende Klammer.

Die Literatur hingegen betont vor allem seine Funktion als Quelle, als öffnende Klammer. Da wird von Backlog, Priorisierung, User Stories, Story Points usw. gesprochen; das alles gehört zum PO als Sender von Aufträgen an das Restteam.

Natürlich, es geht nicht ohne klare Aufträge. Anforderungsdefinition ist eine rechte Kunst. Da braucht es auch viel Dialog zwischen Restteam und PO.

Aber die Anforderungsdefinition ist nicht die wichtigste Aufgabe des PO, weil sie nur eine Folge der wichtigsten Aufgabe ist. Der PO will nicht beauftragen, sondern in Empfang nehmen. Nur weil er in Empfang nehmen will, muss er wohl oder übel beauftragen. Aber je weniger Aufwand er darauf verwenden muss, desto besser.

Das Abnehmen, das Akzeptieren ist aber nicht nur für den PO bzw. den weiter downstream gelagerten Anwender/Kunden wichtig, sondern auch für das Restteam.

Indem der PO nämlich zuallererst sich auf das Akzeptieren konzentriert, stiftet er für das Restteam Sinn. Dadurch, dass er geradezu sehnsüchtig auf die nächste abzunehmende Veränderung wartet, weiß das Restteam, wer es braucht, wofür es sich anstrengt. Der PO als schließende Klammer stellt ein Ziel dar, auf das sich jeder ausrichten kann. Kohärenz wird geschaffen. Insofern ist der PO sogar als leader anzusehen.

Folgen eines Missverständnisses

Leider wird die Rolle des PO meist missverstanden. Entweder wird er nicht als die wichtigste Rolle angesehen. Entwickler scheinen am wichtigsten, immerhin produzieren die das “Objekt der Begierde” für den Kunden. So schwer kann das doch auch nicht sein, oder? Am besten wissen es womöglich sogar die Entwickler, was gebaut werden sollte.

Das Resultat ist ein insgesamt schwacher PO. Er ist halbherzig bei der Sache - wahrscheinlich, weil er sich erstens diese Aufgabe nicht gewünscht hat und zweitens auch noch eine Menge anderer Dinge zu tun hat.

Ein schwacher PO - oder noch schlimmer: ein fehlender - führt dann zu schlechtem Input für das Restteam. Nach der Gleichung garbage in = garbage out führt das wiederum zu schlechtem Output. Und der wiederum führt zu Nachbesserungen. Das verringert die Kapazität des Restteams für neue Features, das erhöht die Verschwendung durch Produktion von Unnötigem und das nagt am Vertrauen des Kunden, weil der nur unzuverlässig und mit suboptimaler Qualität beliefert wird. Und zu allem Überfluss steigen noch die Supportkosten und es sinkt die Motivation.

Oder, selbst wenn der PO als wichtig angesehen wird, dann im üblichen Sinn: als sprudelnder Quell von Anforderungen. Er nimmt dann seine Aufgabe ernst und produziert User Stories, dass es nur so eine Freude ist. Er spricht mit dem Support, er spricht mit dem Kunden, er ist da für Fragen vom Restteam. Das Backlog wächst, die Entwicklung hat zu tun. Die Klammer ist geöffnet.

Doch am Ende… da fehlt es an Zugkraft. Der PO beauftragt stark - und zieht am Ergebnis zu schwach. Er hat wenig Erwartung, wann etwas geliefert werden soll. Und wenn, dann lässt er sich einladen zu einer Präsentation dessen, was das Team meint, präsentieren zu können. Im Zweifelsfall hat er womöglich sogar nicht einmal genügend Entscheidungskompetenz, um Abweichungen “vom Plan” akzeptieren oder ablehnen zu können.

Aus womöglich ordentlichem Input wird auf diese Weise Output von ungewisser Qualität. Da der PO nicht maximal daran interessiert zieht, gibt es endgültiges (!) Feedback oft erst Tage oder Wochen nach Fertigstellung.

Das führt zu Ungewissheit, Motivationsschwund, Verschwendung. Die Zuverlässigkeit des Restteams wird auf diese Weise auch nicht erhöht.

Die Qualität des Teams bestehend aus PO + Restteam(Programmierung, QS) steht und fällt also mit dem PO. Ein schwacher PO wird genauso vom Restteam erkannt wie ein schwacher Lehrer oder schwache Eltern. Der mag dann nett sein - aber systematisch und verlässlich für den Kunden muss man dann nicht produzieren.

Fazit

Softwareentwicklung ist kein Ponyhof. Es ist ein hartes Business, in dem man umso besser besteht, je klarer die Richtung ist. Einer, der Richtung vorgibt, ist der PO. Das tut er aber nicht durch Backlog-Einträge, sondern durch seinen unermüdlichen Willen, produziertes Abzunehmen.

Das hat auch einen ökonomischen Grund: Statt neue Aufträge in das Restteam zu stopfen, sollten angefangene Aufträge herausgezogen werden. Es ist sonst unklar, ob der Arbeitseinsatz gelohnt hat. Nur kann auch Wert für den Kunden entstehen. Den freut ja nicht, wenn das Restteam busy ist, sondern nur, wenn er etwas brauchbares in die Hand bekommt.

Ich habe hier den Begriff PO gebraucht, weil er sich für diese Rolle eingebürgert hat. Ich sehe die aber nicht an Scrum geknüpft. Und ich vermute sogar, dass rundum agiles Vorgehen weniger wichtig ist als ein starker PO, der weiß, dass die Abnahme seine vornehmste Aufgabe ist.

Ein klares Ziel und klares, schnelles Feedback – das ist das Geheimnis erfolgreicher Softwareentwicklung. Das ist oft der Fall am Anfang eines Softwareprojektes/-produktes. Daran erinnern sich alle immer gern zurück. Mindestens diesen Zustand aufrecht zu erhalten, sollte das Ziel jedes Projekt-/Produktmanagements sein.

Wenn ich Softwareprojekte/-produkte gesehen habe, die im Verhältnis zu ihrer Codesauberkeit gut im Fluss waren, dann hat das immer an starken POs gelegen.

Oder umgekehrt: Wo der PO schwach oder gar abwesend war, hat es immer massiv gehakt - auch wenn das Restteam motviert und kompetent war.

Ein PO wird der Wichtigkeit seiner Rolle also am besten gerecht, wenn er stets zuerst schaut, was es abzunehmen gibt. Das erfragt er aber nicht beim Team, sondern hat eine genaue Erwartung dazu. Denn darüber hat er schon vorher mit dem Team verhandelt - und das nicht vor Wochen, sondern gestern.

Jeden Tag etwas abnehmen - so sollte der Rhythmus des PO sein.

Und nur, wenn dem Restteam die Aufträge ausgehen, denkt er daran, was er als nächstes “einkippen” sollte.

Im Stil des agilen Manifests könnte ich so formulieren: acceptance over specification.

Dienstag, 3. Juni 2014

Klein ist ökonomisch

Es kommt auf die Größe an – zumindest bei der Wartbarkeit (oder besser: Evolvierbarkeit). Über Helge Nowaks Präsentation bin ich auf diesen Text gestoßen und darüber dann auf einen Video-Vortrag:

Viktigste faktorer for å redusere teknisk gjeld - Dag Sjøberg from Smidigkonferansen on Vimeo.

Keine Angst, Sie müssen nun nicht Ihr Norwegisch abstauben. Ich denke, interessante Einsichten lassen sich auch aus den Vortragsfolien ziehen.

Vorab aber die Geschichte hinter dem Vortrag: Es wurde ein Experiment zu Wartungskosten für Softwaresysteme gemacht. Dazu wurde dasselbe System bei vier Softwarehäusern in Auftrag gegeben. Anschließend wurden alle vier Systeme gleichermaßen genutzt und also mit realen Daten befüllt. Und dann hat man allen Softwarehäusern einen Änderungsauftrag erteilt.

Mit dem Experiment sollte herausgefunden werden, welche Merkmale von Software mit guter/schlechter Wartbarkeit korrelieren. Deshalb hat man auch noch Softwarequalitätsexperten befragt, wie sie die Wartbarkeit der Systeme einschätzen.

Das spannende Ergebnis: Die Experten lagen daneben mit ihren Metriken. Vorhersagekraft hatte allein die Zahl der Codezeilen (Lines of Code, LOC).

image

Verblüffend, oder?

Denn dem stehen diese Vorhersagen gegenüber:

image

Systeme B und D sollten die beste Wartbarkeit aufweisen, A und C die schlechteste.

Nun könnten Sie sagen, der Aufwand für B und D lag nicht soviel über dem von A wie C. Für B mit dem besten Vorhersagewert war nur ca. 50% mehr Aufwand als für A nötig. Für D nur knapp 100% mehr. Das sind doch keine großen Missweisungen wenn man an das Verschätzen bei der Softwareentwicklung im Allgemeinen denkt.

Gegen diese Interpretation spricht für mich aber zweierlei: Zum einen ist der Vorhersagewert von A der schlechteste und auch noch für sich genommen grottig. Zum anderen steht hinter den schlechteren Wartungsaufwänden bzw. besseren Vorhersagewerten ein deutlich höherer Anfangsaufwand:

image

Meine Norwegisch-Kenntnisse geben mir ein, hier wird gefragt, ob wirklich mehr Aufwand zu geringeren technischen Schulden (lies: mehr Evolvierbarkeit) geführt hat. Leider muss die Antwort Nein lauten. Die laut Vorhersage besser wartbaren Systeme haben zwar knapp 100% bzw. 250% der Aufwands erfordert, der in den Letztplatzierten gegangen ist. Doch trotzdem hat der sie am Ende deutlich geschlagen.

Bessere Metrikwerte waren also teuer – und haben es nicht gebracht. Das ist bitter, oder?

Das am besten wartbare System hat pro Person am wenigsten Zeit in der Entwicklung gekostet und am wenigsten LOC enthalten.

Ist das eine triviale Aussage? Hm… ich glaube nicht. Denn sonst hätten die Experten ja besser geurteilt.

Schlussfolgerungen

Ich denke, es lassen sich aus dem Ergebnis einige Schlussfolgerungen für die Praxis ziehen. Wenn weniger LOC bessere Evolvierbarkeit bedeuten, dann müssen wir alles (naja, zumindest vieles) daransetzen, weniger LOC herzustellen. Und das geht so:

1. YAGNI

Weniger LOC beginnen nicht so sehr beim Entwickler, sondern eher beim Kunden bzw. beim PO. Er sollte noch schärfer darüber nachdenken, was wirklich, wirklich an Features benötigt wird. Und er sollte in noch dünneren Inkrementen codieren lassen, um schneller sagen zu können “Good enough!”. Ich bleibe also dabei, das Spinning eine Kernpraxis jeder Softwareentwicklung sein sollte.

Code erst gar nicht zu schreiben, ist das beste Mittel, um LOC zu reduzieren.

Aber das geht natürlich nicht nur den PO etwas an, sondern auch die Entwickler. Gern wird auf Vorrat implementiert, weil man ja soviel Erfahrung hat, was da noch alles kommen mag. Dann wird vorhergesehen und flexibilisiert, was das Zeug hält. Wiederverwendbarkeit ist ganz beliebt als herzustellendes Merkmal – auch wenn niemand weiß, was wie viel wiederverwendet wird.

2. KISS

Was dann implementiert wird, sollte so einfach wie möglich sein. Da verschwimmt manchmal die Grenze zwischen YAGNI und KISS, aber ich denke, jeder kennt Beispiele, wo etwas kompliziert gelöst wurde, wo es einfacher gegangen wäre. Der Grund: Unkenntnis, vermeintlicher Zeitmangel.

Auch für Code gilt Pascals (oder Goethes?) Ausspruch: Wenn ich mehr Zeit gehabt hätte, hätte ich mich kürzer gefasst.

Wie das Experiment zeigt, ist Zeitaufwand gesteckt in weniger LOC aber gut investierte Zeit. Die macht die später unabsehbaren “Wartungsarbeiten” kostengünstiger.

Natürlich gilt es da eine Balance zu finden. Mancher elegante Einzeiler ist am Ende schwerer zu evolvieren als etwas gesprächigerer Code.

3. Knappe Programmiersprache

Die LOC-Frage wirft eine zweite, womöglich schon zu den Akten gelegte auf: Welche Programmiersprache sollte benutzt werden, um das Nötige simpel zu codieren?

Früher ging es um Compiler- und Ausführungsgeschwindigkeiten. Heute ist das nicht mehr so ein großes Thema. Stattdessen sollte es um Knappheit (terseness) gehen. Natürlich Knappheit, die zur Lesbarkeit beiträgt. Das kann man von PERL oder APL eher nicht sagen, aber wohl z.B. von F#.

Welche Sprache bietet gute Abstraktionen, welche Sprache bemüht sich um “Rauschunterdrückung”? Java hat da heute für mich z.B. eher einen hinteren Platz. C# scheint mir im Mittelfeld. Und wie steht es mit Modernem wie Go, Exlixir, Swift?

Einerlei. Ich will hier für keine Sprache werben, sondern nur auf ein Mittel zur Reduktion der LOC für mehr Evolvierbarkeit aufmerksam machen. Die Sprachfrage sollte nicht leichtfertig abgetan werden mit dem Verweis aus die Tradition: “Wir haben halt immer schon in X entwickelt.”

4. Granulare Abstraktion

Eine Programmiersprache bietet mehr oder weniger Abstraktion. Mehr bedeutet gewöhnlich weniger LOC. Noch mehr bedeutet noch weniger LOC – aber ab einem gewissen Punkt geht das auf Kosten der Universalität. Beispiel SQL: damit lassen sich sehr knapp Anweisungen ausdrücken, aber SQL ist nicht computationally complete.

Durch die Wahl einer 3GL lassen sich die LOC also nur auf ein gewisses Maß reduzieren. Weiter muss es dann mit DSLs (Domain Specific Languages) gehen. Die gibt es nur nicht für alle Problemdomänen. Und es ist nicht zu erwarten, dass sich das grundlegend ändert. Einzelne Teams haben es schon schwer genug, Bibliotheken mit vernünftigen Abstraktionen zu entwickeln, da sind eigene DSLs in weiter Ferne. Sie haben ihren Platz – aber ihr Beitrag zur LOC-Reduktion für den Mainstream wird gering bleiben.

Wichtiger finde ich es daher, mit den Mitteln der gewählten 3GL eigene Abstraktionen auf unterschiedlichem Niveau zu schaffen. Die Mittel sind Container wie Funktion, Klasse/Modul, Komponente, (micro)Service.

Mein Annahme ist, dass sich das Versuchsergebnis auf verschiedene Größenordnungen übertragen lässt. Zwei Funktionen, die dasselbe leisten, sind je nach LOC unterschiedlich evolvierbar. Zwei Klassen, die dasselbe leisten, sind je nach LOC unterschiedlich evolvierbar. Usw. usf.

Kleine Funktionen, kleine Klassen, kleine Komponenten, kleine Services sind also anzustreben. Funktionen mit 3000 LOC, Klassen mit 100.000 LOC, die Abwesenheit von Komponenten und Services… das alles sind Zeichen der Unwartbarkeit.

Wer Evolvierbarkeit will, der sollte sich also stetig um kleine Container bemühen und um Container auf weiteren Abstraktionsebenen oberhalb von Klassen. Wo es nur Funktion, Klasse, Softwaresystem und das auch noch ohne LOC-Begrenzung gibt, ist der Monolith vorprogrammiert.

Wie kommen Sie denn aber zu kleinen Funktionen und Klassen? Die Forderung ist ja auch schon älter, ohne dass sie einen spürbaren Effekt gehabt hätte. Manche sagen, Funktionen sollten nur 7 oder 20 Zeilen haben. Andere sagen, sie sollten nicht länger als eine Bildschirmseite sein (aber mit welcher Auflösung).

Ich glaube, die Forderung nach einem bestimmten max. Zeilenzahl bringt uns nicht weiter. Wir brauchen einen Ansatz des Softwareentwurfs, der ganz natürlich zu kleinen Funktionen und Klassen führt. Die bisherige Objektorientierung leistet das nicht. Wir müssen nachbessern.

In meiner Codierungspraxis gibt es inzwischen keine Funktion mehr, die länger als 50 Zeilen ist (und mehr als Triviales tut). Nicht, weil ich mich durch StyleCop warnen lasse, sondern weil ich Software mit Flow-Design nur so entwerfen kann, dass alle Funktionen klein sind. Wenn Sie wissen wollen, wie das funktioniert, folgen Sie doch dem “Book in Progress” von Stefan Lieser und mir: The Architect´s Napkin Kata für Kata.

Eine größere Zahl von Funktionen und Klassen braucht dann natürlich wieder Container. Das Softwaresystem als Ganzes ist zu groß. Deshalb gehören für mich Komponenten- und Serviceorientierung ganz natürlich zu den Maßnahmen, die LOC pro Container zu reduzieren. Die einzuführen ist aber natürlich nochmal eine andere Nummer als die LOC-Reduktion bei den schon in Gebrauch befindlichen Containern Funktion und Klasse.

Aber es lohnt sich, denke ich. Durch Komponenten und Services wird es nicht nur mit LOC besser pro Container, sondern auch mit der Produktionseffizienz. Denn durch die expliziten Kontrakte kann an Komponenten und Services viel besser arbeitsteilig parallel gearbeitet werden. Darüber hinaus bieten Services die Chance, im selben Softwaresystem mehrere Runtimes/3GLs einzusetzen. Plattformneutrale Kontrakte machen es möglich. Ausführlicher dazu in The Architect´s Napkin – Der Schummelzettel.

Ziel ist in jedem Fall, mit den vielen kleinen Containern ein eigenes Domänenvokabular, gar eigene “Domänensätze” zu formulieren. Mit dem können Sie dann immer neue Geschichten (user stories) “schreiben”.

5. Geschichten trennen

Container sind ein technisches Granulat. Ich denke, es gibt aber auch aus Sicht von Kunde/Benutzer die Möglichkeit, granularer zu entwickeln, d.h. mehr Funktionseinheiten mit jeweils weniger LOC zu bilden.

Das Problem beginnt hier wieder beim Kunden. Der will oft “alles unter einem Hut”. Der sucht die “one size fits all” Anwendung. Die wächst und wächst dann und enthält alle LOC.

Was aber, wenn die Anforderungen nicht durch nur eine Anwendung, sondern durch viele umgesetzt würden. Nicht ein Icon auf dem Desktop, nicht eine URL im Intranet für alles, sondern viele.

Die Smartphones machen es vor. Statt einem Boliden wie Outlook vertrauen wir uns einem Schwarm aus kleinen Apps für Email, Kalender, Aufgaben, Notizen an. Viele Spezialisten statt ein Generalist. Das sind viele kleine Zweige unabhängiger Evolutionsmöglichkeit.

Jede App ist auf eine Domäne spezialisiert, erzählt sozusagen eine eigene Geschichte. Warum nicht dasselbe tun mit CRM, ERP, Buchhaltung, Qualitätsmanagement, Vertragsverwaltung usw. usf.? Das gesamte Softwaresystem enthält dann immer noch 100% aller LOC – nur ist es unterteilt in Apps mit jeweils einem Bruchteil der LOCs. Die lassen sich für sich leichter evolvieren.

Und innerhalb von Apps können Sie weiter Geschichten trennen. Apps müssen keine Monolithen sein, sondern können aus Modulen bestehen.

Am Ende geht es also nicht um ein Softwaresystem, sondern um einen bunten Strauß ein Modulen in Apps zusammengefasst. Das ist dann nicht eine monolithische Codebasis. Vielmehr gibt es viele schmale Schnitte durch die Anforderungen: Durchstiche. Jeder macht für sich Sinn. Jeder kann getrennt von anderen evolvieren. Änderungsaufträge werden sich meist auf einzelne Module beziehen. Die gesamte Codebasis steht also gar nicht mehr zur Debatte.

Fazit

Ich denke, das Ergebnis des Experiments bestätigt ein Bauchgefühl, das wir immer hatten. Zeit, dass wir es ernst nehmen. Packen wir die LOC bei den Hörnern und reduzieren, wo wir können.

Wo der LOC-Haufen schon groß ist, geht es nur mit Refaktorisierungen. Wo immer wir aber neuen Code schreiben, sollten wir uns mit den beschriebenen Mitteln bemühen, die LOC gering zu halten. Prevention over refactoring ;-)

Kleiner Code, Code mit weniger LOC ist unterm Strich einfach ökonomischer. Das zählt für unsere Kunden.