Mittwoch, 21. November 2012

Spekulatives Coworking

Heute probiere ich mal einen Coworking Space aus. Places Hamburg heißt der. Ist noch so neu, dass die Handwerker im Hintergrund wurschteln und auch mal den Strom abklemmen. Naja, macht nix, dafür kostet das Coworking bis Ende November 2012 hier nichts. Dafür darf zwischendurch der Akku in meinem MacBook Air übernehmen.


Dass es an meinem Fensterplatz ein wenig zugig ist, weil die Heizung komplett unterhalb des massiven "Tischbrettes"ist, nehme ich auch in Kauf. (Manchmal ist es schon merkwürdig, wie Funktionalität für Design geopfert wird. Was Innenarchitekten sich so denken... oder eben auch nicht...)


Coworking-Atomphäre will ebenfalls noch nicht aufkommen. Ich bin der einzige der hier arbeitet und nicht dazu gehört. Ansonsten nur zwei ältere Damen im Café, das auch zu Places Hamburg gehört. Es ist eher lone working. Noch. Denn bestimmt werden sich über die nächsten Wochen weitere Coworking-Suchende einstellen.


Für heute stört mich das alles nicht. Ich komme mal raus aus meiner Home Office und sehe etwas anderes als mein übliches "Büro", das elbgold Café am heimischen Mühlenkamp nahe der Alster.

Doch ich nehme meine Erlebnis hier mal als Anlass darüber zu spekulieren, wie für mich eigentlich Coworking aussehen müsste. Was wäre für mich attraktiv als Alternative zu Home Office und normalem Café?

Zuhause arbeiten zu können, ist schon ein Geschenk. Alles, was ich brauche, ist zur Hand. Für Raum, Infrastruktur (WLAN, Drucker, Scanner usw.) und Verpflegung habe ich schon gezahlt. Sobald ich woanders arbeite - außer beim Kunden - können meine Kosten nur steigen. Wenn ich mich dafür entscheide, sollte also etwas dafür herausspringen. Ich will was bekommen für meine zusätzlichen Kosten.

Man könnte mich mit etwas locken, das ich zuhause habe (oder auch nicht) und mir der Coworking-Space auch bietet. Ganz oben auf der Liste der Coworking-Space Anbieter ist da ein Arbeitsplatz (Tisch, Stuhl), Strom und WLAN.

Hm... da bin ich allerdings nicht so beeindruckt. Tisch und Stuhl und sogar Strom bekomme ich in fast jedem Café oder einer Bibliothek auch. WLAN? Nein, tut mir leid, habe ich in ausreichender Geschwindigkeit in der Tasche. Mein iPhone mit 3G als Router reicht mir innerhalb von Großstädten an den meisten Plätzen.

Wofür also bei Places Hamburg 25 EUR pro Tag ausgeben? Oder beim betahaus 15+ EUR/Tag, im Werkheim 15 EUR/Tag, im @Lilienhof 45 EUR/Tag?

Wenn ich mehr Platz und vor allem "neutralen" Platz brauche, wo ich mich mal mit Kollegen/Kunden zusammensetzen kann... dann will ich einsehen, dafür etwas zahlen zu müssen. Das geht über das hinaus, was ich zuhause habe.

Oder wenn ich etwas geboten bekäme, was ich mir nicht leisten kann, z.B. ein SMART Board, dann würde ich das auch einsehen.

Aber für einen Preis ab 15 EUR bekomme ich... Strom, Tisch, Stuhl. WLAN. Das war´s?

Im elbgold, die mich gern auch einen ganzen Tag unbehelligt arbeiten lassen, bekomme ich für 15 EUR deutlich mehr. Nämlich zum Beispiel 5 x einen leckeren Chai oder 1 x Kuchen + 1 x Lachs Bagel + 2 x Chai.

Achja, da war noch etwas bei den Coworking Spaces: Es heißt ja Co-Working, also irgendwie geht es auch um Arbeit mit anderen gemeinsam. Man bietet eine Bude voller Leben, eine bunte Mischung aus Arbeitenden. Klar, das habe ich nicht zuhause - außer meine Tochter ist da und macht Hausaufgaben.

Ja, zuhause ist es manchmal etwas still und einsam. Deshalb gehe ich auch ins "Büro". Da treffe ich Menschen, die ich kenne. Wir plaudern dann. Oder ich sehe viele Menschen, die mir eine Art "rauschenden Hintergrund" für meine Arbeit bieten. Mir macht das nichts aus, wenn an allen Tischen geredet wird. Dabei kann ich mich wunderbar konzentrieren - und fühle mich irgendwie auch als Teil einer Gemeinschaft. Dass das keine ebenfalls arbeitenden Menschen sind, sondern solche, die ins Café zur Entspannung gehen, macht nichts. Soviel will ich gar nicht mit anderen über meine Arbeit reden.

Ein Coworking Space will mir nun das auch bieten: eine Gemeinschaft der Arbeitenden, der etwas Gesellschaft Suchenden. Und sogar noch mehr will der Space sein, nämlich einer, in dem sich auch Kontakte ergeben, in dem es zu überraschenden Netzwerkverknüpfungen und Zusammenarbeit kommt.

Tolle Sache. Dass sich im elbgold für mich schon ein halbes Dutzend schöner Zusammenarbeiten und Freundschaften entwickelt haben, will ich dagegen gar nicht anführen. Ich frage mich nur, warum ein Coworking Space nur dafür von mir 15 EUR oder mehr haben will?

Zugegeben, der Arbeitsplatz im Coworking Space ist eben ein Platz zum Arbeiten. Das ist manchmal ein bisschen bequemer als in einem Café. Wieviel möchte ich dann aber für solche Bequemlichkeit ausgeben?

Ich will auch einsehen, dass ein Café wahrscheinlich ungern alle seine Plätze von Leuten wie mir belegt sehen möchte. Ich verzehre auf meinem Stuhl über einen Tag weniger als würden Gäste darauf im Stundentakt wechseln.

Aber genug der Kritik. Ich schalte mal in den Spekulations- und Fabuliermodus. Was fände ich denn nun attraktiv? Tisch, Stuhl, Strom, WLAN führe ich nicht auf. Die sind selbstverständlich. Damit kann ein Coworking Space gegenüber einem Café nicht punkten.

Stattdessen würde mich reizen...
  • Ein Ort, an dem ich für mein Geld den Tag über versorgt bin. Ich möchte mich auf meine Arbeit konzentrieren und nicht auf "Nahrungssuche". Das bietet ein Café nur begrenzt. Mir geht es um einen gesunden Mittagstisch mit etwas Auswahl, gesunde Snacks und ein paar "Grundgetränke": das sollte im Preis enthalten sein.
  • Ein Ort, an dem ich meinen Modus wechseln kann. Stillarbeit am Tisch, Überlegen am Whiteboard, Diskutieren in kleiner Runde etwas separat. Das sollte nach Verfügbarkeit bzw. timeboxed im Preis enthalten sein.
  • Ein Ort, an dem ich Mittel nutzen kann, die ich sonst nicht habe. Ein SMART Board steht für mich da ganz oben auf der Liste. Heute noch einen Konferenzraum ohne auszustatten, finde ich anachronistisch. Beamer und Flipchart sind zu wenig. Andere Mittel wären Zugänge zu kostenpflichtigen online Angeboten, beispielsweise IEEE oder ACM. Die könnten auf Pad-Computern eingerichtet sein, die man sich ausleihen kann (solange der Vorrat reicht). Oder warum nicht eine Lego Mindstorm Ecke? Oder Raspberry Pi? Oder eine AR Drone? Halt geeky stuff, den ich sonst nicht habe. Daran könnte ein Coworking Space auch weitere Angebote hängen, z.B. Workshops zur Einführung in die Programmierung von solchen Technologien oder Wettbewerbe. Halt Community Events, die gern etwas kosten dürfen. [1]
  • Ein Ort, der skaliert, wo ich jenseits des Vorgenannten auch mehr Raum bekommen kann. Dafür bezahle ich dann gern mehr.
Muss das alles stylish sein? Darauf legt Places Hamburg nämlich wert. (Allerdings fühle ich mich hier eher wie im Einrichtungshaus. Die Nüchternheit ist mir ein bisschen zu modern, glaube ich. Das private Cinema im Keller jedoch... mit gemütlichen Sesseln... das finde ich cool.)

Nein, speziellen Style brauche ich nicht. Auf meinem Platz muss ich es einen Tag gut aushalten können ohne Rückenschmerzen. Das reicht. Biergartenbänke wäre da wohl zuwenig. Ansonsten jedoch kann es einfach, funktional sein, wenn das die Kosten überschaubar hält.

So, wie es aussieht, gibt es einen derartigen Coworking Space in Hamburg derzeit wohl nicht. Die Verpflegung muss extra bezahlt werden oder ist minimal. Der Moduswechsel ist nur begrenzt möglich ohne Aufpreis. Und besondere Technologie sehe ich weit und breit nicht.

In Summe sind Coworking Spaces für mich also noch nichts. Sie sind mir für ihr - sorry to say - hausbackenes Angebot zu teuer im Vergleich zu meinem Home Office oder einem Café. Nach dieser Spekulationsrunde weiß ich jedoch, warum ich mich bisher dafür nicht erwärmen konnte.

Wie sich ein Coworking Space nach meinem Geschmack finanzieren ließe, weiß ich grad auch nicht aus dem Stand. Muss ich aber auch nicht ;-) Ich wünsche mir einfach nur mal etwas. Jedenfalls wäre ich bereit, dafür einen monatlichen Grundbetrag auszugeben, z.B. 10-20 EUR. Und dann pro (halbem) Tag noch etwas dazu, z.B. 15-20 EUR - wenn ich dafür versorgt bin. Das ließe sich ja auch mit meinem heimischen Budget für Frühstück/Mittag oder so verrechnen.

Hm... vielleicht sollte ich jetzt mal auf die Suche gehen, ob es noch andere gibt, die sich Ähnliches wünschen. Dann machen wir den Geek Space Hamburg auf :-)

Fußnoten

[1] Einige dieser Mittel mögen speziell interessant für Softwareentwickler sein. Aber warum auch nicht. Wenn die Flash Designer oder Grafik Designer andere wollen, dann nur zu. Vielleicht bilden sich dann Coworking Spaces für Branchen statt ganz allgemein für Freiberufler.

Montag, 12. November 2012

Aspektvolles Programmieren

Neulich habe ich kritisiert, Lösungen für Code Katas seien oft schwer verständlich. Als Ursache dafür habe ich einen Mangel an herausgearbeiteten Aspekten identifiziert.

Mit Code Katas wird üblicherweise TDD geübt. Das hatte Dave Thomas zwar nicht speziell im Sinn, als er zu Code Katas ermunterte, doch es ist die de facto Praxis in Coding Dojos. Explizit hat Dave Thomas gesagt:

“Some involve programming, and can be coded in many different ways. Some are open ended, and involve thinking about the issues behind programming.” (meine Hervorhebung)

Dass über die Themen hinter der Programmierung nach-ge-dacht würde… Nein, tut mir leid, das habe ich in keinem Coding Dojo bisher erlebt, weder in einem offenen, noch in einem inhouse Dojo. Der Goldstandard ist vielmehr, dass schon Rechner und Beamer eingerichtet sind, kurz das Organisatorische besprochen wird, man sich flux über die Aufgabe austauscht – max. 15 Minuten – vor allem aber zügig das (Pair) Programming beginnt. Und damit endet dann ein eventueller “Nachdenkmodus” endgültig.

Klar, es gibt Diskussionen über die nächsten Schritte. Aber so richtig ins Nachdenken über “issues behind programming” kommt da niemand mehr. Ist ja auch nicht das ausgelobte Ziel. Das lautet vielmehr, die Lösung möglichst weit mit TDD voran zu bringen. [1]

Und so sehen dann die Lösungen aus. Man bekommt, was die Regeln vorgeben: grüne Tests. Das ist nämlich das einzige, was irgendwie “gemessen” wird. Darüber gibt es keinen Zweifel. Schon die Qualität der Tests ist nicht mehr leicht zu ermitteln. Sie ergibt sich eher implizit dadurch, ob der Fortschritt leicht oder schwierig ist. Denn sind die Tests schlecht geschnitten und ungünstig priorisiert, wird das Codieren zäh.

Aber wie denn anders?

Nun, es kommt erstmal aufs Ziel an. Was soll denn mit einer Code Kata erreicht werden? Wenn das ausschließlich lautet “TDD-Fingerfertigkeit entwickeln”, dann ist ja alles gut. Weitermachen mit Dojos wie bisher.

Nur finde ich das inzwischen erstens langweilig und zweitens am Hauptproblem vorbei. Langweilig, weil nach 5+ Jahren Coding Dojos auch mal was anderes dran sein dürfte. Oder findet das sonst niemand zum einschlafen, wenn erwachsene Männer womöglich Monat für Monat zusammenkommen, um diese Fingerfertigkeit zu entwickeln. Mit Verlaub: Das ist doch viel, viel simpler, als aus einer Karate Kata das Letzte herauszuholen.

Vor allem löst diese spezielle Variante automatisierter Tests nicht das Hauptproblem der Codeproduktion. Das ist mangelhafte Evolvierbarkeit von Code.

Nicht, dass es keine Teams gäbe, die nicht von automatisierten Tests profitieren könnten. Davon gibt es noch viele. Die sollen da auch möglichst schnell Fuß fassen. Nur ist deren Einführung erstens technisch kein Hexenwerk: NUnit runterladen, referenzieren, los geht´s. Und zweitens ist die konkrete Praxis automatisierter Tests relativ unbedeutend. Ob nur “irgendwie” test-first oder richtiggehendes TDD oder Tests nach der Implementation geschrieben… klar, die Ergebnisse sind unterschiedlich, doch entscheidend ist nicht dieser Unterschied durch die Stile, sondern der zur vorherigen Testlosigkeit.

Warum fällt es denn aber Teams auch heute noch schwer, überhaupt automatisierte Tests einzusetzen? Weil sie keine TDD-Fingerfertigkeit haben? Nein, weil sie keine Codebasis haben, die das (nachträgliche) Anbringen von Tests erlaubt. Und das kommt daher, dass sie nur wenig Vorstellung davon haben, wie eine saubere, testbare Codebasis überhaupt aussieht.

Damit sind wir beim blinden Fleck der TDD-Coding-Dojo-Praxis. Über dieses “issue behind programming” wird nicht nachgedacht. Das Refactoring im TDD-3-Schritt red-green-refactoring ist für das übliche Coding Dojo eine Black Box. Deren Deckel wird höchstens ein wenig imageangehoben. Besser aber man lässt sie geschlossen. Es könnte eine Büchse der Pandora sein. Wenn die erstmal geöffnet ist, käme man mit dem Codieren ja gar nicht mehr voran – oder würde gar nicht erst beginnen.

Das halte ich für sehr bedauernswert. Das möchte ich ändern.

TDD ist gut und schön. Wem das noch nicht langweilig ist, wer glaubt, dass alles besser wird, wenn er darin nur perfekt ist… der soll gern TDD hoch und runter üben. Mir käme das allerdings so vor, als würde jemand meinen, ein großer Kung Fu Meister zu werden, weil er den ganzen Tag am Mu ren zhuang arbeitet.

Zur Programmierkunst gehört aber doch mehr. Wenn nicht, wäre sie armselig und könnte schon bald von jedermann ausgeübt werden.

Was das ist, das mehr dazugehört? Damit meine ich nicht die unendliche Vielfalt an Technologien. Die ist sicherlich eine große Herausforderung. Doch letztlich ist das eher eine Frage der Masse. Das sind komplizierte Oberflächlichkeiten.

Viel, viel schwieriger sind jedoch die “issues behind programming”. Da geht es um Paradigmen. Da geht es um Methoden. Da geht es um eine Vielzahl von Qualitäten über die “simple” Korrektheit von Code hinaus.

TDD adressiert die vermeintlich. Aber in Wirklichkeit adressiert sie nicht TDD selbst, sondern das Refactoring, auf das sich TDD stützt. Nur wird eben genau das nicht geübt. [2]

Nun aber genug mit dem TDD bzw. Coding Dojo Bashing ;-) Ich will mich ja auch nicht zu sehr wiederholen.

Wie stelle ich es mir denn nun anders vor? Wie hätte ich mir ein Vorgehen zum Beispiel in Bezug auf die Kata Word Wrap gewünscht?

1. Domäne spezifizieren

Als erstes sollte gefragt werden, was denn wirklich, wirklich die Problemdomäne ist. Worum geht es? Und das sollte dann in einem Satz knackig formuliert werden.

Schon bei der Kata Word Wrap ist das nicht ganz einfach. Ich habe mich da auch erst verlaufen. Der Titel ist nämlich fehlleitend. Er passt nicht zur Aufgabenstellung.

Im Titel kommt Word Wrap vor. Damit scheinen Worte und ihr Umbruch im Kern der Ubiquitous Language zu stehen. Interessanterweise tauchen dann Worte in der Aufgabenbeschreibung eigentlich nicht mehr auf. Da steht zwar “You try to break lines at word boundaries.” – doch zentral ist nicht “word”, sondern “break lines”.

Es geht nicht um Word Wrap, sondern um Zeilen(um)bruch. Das mag zunächst im Gespräch ein feiner Unterschied sein – doch in der Implementation würde der sich ausweiten.

Ich habe mich auch verleiten lassen, eine Wortumbruchlösung zu suchen, statt einer für den Zeilenumbruch. Doch der geht an der Aufgabe vorbei. [3]

Mein Vorschlag für eine knackige Beschreibung der Problemdomäne wäre dann zum Beispiel diese:

Der Wrapper zerlegt einen einzeiligen Text in mehrere Zeilen einer Maximallänge zerlegen - wobei Worte nicht zerschnitten werden sollen, wenn es sich vermeiden lässt.

Diese Beschreibung betont die Zeilenorientierung. Worte kommen darin nur insofern vor, als dass sie nicht “beschädigt werden sollen”. Die Zerlegung soll zu keinen “Kollateralschäden” führen.

Ob diese Formulierung die beste ist, sei dahingestellt. Aber sie ist besser als keine. Die Kraft überhaupt einer knackigen Formulierung ist nicht zu unterschätzen. Sie richtet das weitere Denken aus, sie spannt einen Lösungsraum auf – und zieht damit gleichzeitig eine Grenze. So kann konstruktive Diskussion über einen Lösungsansatz entstehen. Denn die kann, um sich fokussiert zu halten, immer wieder auf diese knappe Spezifikation verweisen. Der Zweck ist also ähnlich der der Metapher im XP.

2. Lösung skizzieren

Mit der Beschreibung der Problemdomäne als Leitstern und der Anforderungsdefinition als Karte, sollte als nächstes ein Lösungsansatz erarbeitet werden. Das geschieht im Wesentlichen durch Nachdenken. [4]

Die Frage lautet: Wie würde ich das Problem lösen, wenn ich es von Hand tun müsste?

Bei der Beantwortung soll natürlich jede Art von Erfahrung eingebracht werden. Alles ist erlaubt. Das Ergebnis kann ebenfalls alles mögliche sein – nur kein Produktionscode.

Früher gab es für algorithmische Aufgabenstellungen die ehrenhaften Mittel Flowchart, Structogramm und vor allem Pseudocode. Heute scheint das nicht mehr hip zu sein. Überhaupt sehen Lösungsskizzen seltsam holprig aus, wo sie denn überhaupt externalisiert werden können. Meist existieren sie ja nur sehr diffus in den Köpfen der Entwickler – und niemand weiß so recht, ob alle denselben Lösungsansatz meinen.

Ich sehe da ein großes Defizit bei unserer Zunft. Die Kunst, Lösungen zu beschreiben, ohne sie gleich zu codieren, ist sehr schwach ausgeprägt.

Dabei verlange ich nichts Spezielles. Mir ist es im Grunde egal, wie man einen Lösungsansatz beschreibt. Er soll nur auf einem höheren Abstraktionsniveau als die spätere Implementierung liegen. Sonst hat man ja nichts gewonnen. Nur über “Skizzen” lässt sich effizient und effektiv diskutieren; nur sie lassen sich in einer Gruppe gemeinsam entwickeln. Code selbst ist dagegen zäh. Man verliert sich auch zu schnell in technischen Details und verliert das Big Picture der Lösung aus dem Blick.

Für die Kata Word Wrap könnte die Lösungsskizze zum Beispiel in einer Schrittfolge bestehen. Nix Grafik, nix Tool, nix großer Aufwand. Einfach nur mal hinschreiben, wie man schrittweise das Problem eines Zeilenumbruchs lösen könnte. Beispiel:

    1. Vom gegebenen Text bricht man eine Zeile von Maximallänge vorne ab. Es entstehen eine Zeile und ein Resttext.
    2. Dann schaut man, ob diese Zerlegung dazu geführt hat, dass ein Wort zerschnitten wurde. Falls ja, macht man die rückgängig: Es wird der abgeschnittene “Wortkopf” am Ende der Zeile zurück an den Anfang des Resttextes zum “Wortrumpf” versetzt.
    3. Die Zeile kann jetzt an den bisher schon erzeugten umgebrochenen Text angefügt werden.
    4. Sofern noch Resttext übrig ist, den als neuen gegebenen Text ansehen und damit genauso wie bis hierher verfahren.

Diese Lösungsschritte zu finden, ist doch kein Geniestreich, oder? Aber nun kann jeder im Team prüfen, ob er versteht, was die Aufgabe ist und wie eine Lösung aussehen könnte. Es kann Einigkeit im Team hergestellt werden. Ein Ziel kann anvisiert werden.

Und wer diese Lösung nicht gut genug findet, der kann jetzt aufstehen und eine Diskussion anzetteln. Gut so! Das ist nämlich auf dieser konzeptionellen Ebene viel einfacher, als wenn erst Berge an Code produziert sind.

Sobald die IDE angeworfen ist, sind alle im Codierungsbewusstseinszustand. Dann ist Diskussion über Grundsätzliches nicht erwünscht. Das finde ich auch total verständlich. Denn Lösungsfindung und Codierung der Lösung sind zwei ganz unterschiedliche Aspekte von Softwareentwicklung. [5]

Nicht nur bei Code ist es also klug, Aspekte zu trennen, sondern auch im Entwicklungsprozess.

3. Lösungsaspekte identifizieren

Aber erstmal zu den Aspekten der Lösung. Wie lauten die? Wie umfangreich sind sie? Wie stehen sie im Zusammenhang?

Im vorliegenden Beispiel sind die Aspekte:

  • Textzerlegung und
  • Textzusammenbau.

Zur Zerlegung gehört das Abschneiden einer Zeile vom gegebenen Text und die eventuell nötige Korrektur dieses Schnitts. Der Textzusammenbau besteht dann nur noch aus dem Anhängen der endgültigen Zeilen an einander.

Durch die Identifikation der Aspekte wird die Lösung nochmal auf ein höheres Abstraktionsniveau gehoben. Hier kann das Team wieder prüfen, ob sie sich stimmig anfühlt. Ja, es geht ums Gefühl, um Intuition, um Erfahrung. Denn bisher ist kein Code geschrieben.

Aber das macht nichts. Entwickler geben ja immer soviel auf ihre Erfahrung. Dann ist hier der richtige Zeitpunkt, sie mal einzusetzen.

Dass diese “Gedankenblasen” nicht crashen… Klar, das ist so. But it´s not a bug, it´s a feature. Würden sie crashen, wären sie Code. Und Code ist vergleichsweise starr – egal ob mit TDD oder sonstwie entwickelt.

Nachdenken, eine konzeptionelle Lösung finden, ist erstens eine Tätigkeit, die das Team zusammenführt. Und zweitens ist es eine qualitätssichernde Maßnahme. Es wird damit nämlich die Qualität des Input für den Engpass “Implementierung” im Softwareentwicklungsprozess angehoben.

Codieren ist kein Selbstzweck. Also sollten wir nicht möglichst viel Code, sondern möglichst wenig Code schreiben. Damit meine ich aber nicht möglichst wenige LOC, sondern möglichst wenige Überarbeitungen. Sozusagen “LOC in place” vs “LOC over time”.

Hier der Überblick über die Aspekte und ihre Zusammenhänge:

2012-11-10 08.35.34

So ein Bild kann man leicht kommunizieren. Es enthält die Essenz der verbalen Lösungsskizze. Und es zeigt deutlich die Aspekte. Wenn jetzt zum Beispiel ein neues Teammitglied an Bord käme, könnte es anhand dieses Bildes leicht in den Lösungsansatz eingeführt werden. Warum den also nicht dem Code beigeben? Oder versuchen, ihn im Code möglichst nachvollziehbar festzuhalten?

Ja, genau, dieser Entwurf sollte sich im Code widerspiegeln. Das ist nicht umsonst ein Clean Code Developer Prinzip. Denn wenn der Entwurf nur für den einmaligen Codierungsgebrauch gemacht würde und danach verschwände, müssten Entwickler in Zukunft wieder Codearchäologie betreiben, um herauszufinden, wie das denn alles gemeint war.

Zu sehen ist im Bild eine Aspekthierarchie. Zuoberst der umfassende Aspekt der Domäne, d.h. der Gesamtheit der Anforderungen.

Darunter die beiden aus der Lösungsskizze abstrahierten Aspekte.

Und auf der untersten Ebene deren Sub-Aspekte, die konkreten Operationen. Die Doppelpfeile deuten an, wie sie horizontal zusammenhängen, um die funktionalen Anforderungen zu erfüllen.

Das ist doch nicht schwer zu verstehen, oder? Da muss man kein UML-Studium absolviert haben. Man muss auch nicht OOP- oder FP-Jünger sein. Gesunder Menschenverstand reicht aus. Eine umfassende Aufgabe wurde hier nachvollziehbar zerlegt in kleinere Aufgaben. Das ist ein probater Ansatz bei einer so algorithmischen Aufgabenstellung.

4. Lösung codieren

Mit der Lösungsskizze in der Hand, kann man sich ans Codieren machen. Große Überraschungen sollten sich bei der geringen Komplexität nicht einstellen. Das Codieren kann geradlinig ablaufen. Ob mit TDD oder ohne? Ich sag mal: Zuerst einen oder mehrere Tests für eine Operation zu definieren, bevor man sie implementiert, ist schon eine gute Sache. Das kleinschrittige Vorgehen wie bei TDD jedoch, halte ich in diesem Fall nicht für zwingend. Also: Test-First, ja; TDD, nein.

Und mit welcher Funktionseinheit beginnen?

Das Schöne an der Lösungsskizze ist, dass man beliebig reingreifen kann. Man kann zum Beispiel mit dem Anfügen einer Zeile an den schon umgebrochenen Text beginnen. Hier die Tests zuerst:

image

Und hier die Implementation:

image

Das ist überschaubar. Das bringt schnell ein Erfolgserlebnis. Das lässt sich auch sofort in eine Wrap()-Funktion einsetzen…

image

um erste Integrationstests zu erfüllen:

image

Ist ja nicht so, dass bei diesem Vorgehen nicht in Durchstichen, d.h. in Nutzeninkrementen gedacht werden soll.

Aber warum nicht den Code direkt in Wrap() schreiben und später raus-refaktorisieren? Weil wir uns auch nicht dümmer stellen müssen, als wir sind. Wir wissen schon, dass das ein eigener Aspekt ist und andere hinzukommen werden. Und Aspekte kapselt man mindestens in eigene Methoden.

Der aspekteigene Test ist ein Unit Test. Wenn später mal etwas schiefgehen sollte, dann zeigen solche Unit Tests viel besser als Integrationstests, wo das Problem liegt. Der Test von Wrap() hingegen ist ein Integrationstest, weil er viele Aspekte auf einmal prüft.

Und so geht es dann weiter: Jeder Aspekt kann für sich codiert werden. Naja, jede Operation, also die Aspekte auf der untersten Ebene bei der obigen Zeichnung. Die darüber liegenden Aspekte integrieren diese, sie sind abhängig. Deshalb ist wohl tendenziell eine bottom-up Codierung angezeigt.

In jedem Fall entstehen Operationsmethoden, die jede für sich getestet sind. Wenn Sie die mit TDD implementieren wollen… ist das keine schlechte Idee. Ich bin ja nicht gegen TDD. Nur schmeckt mir der “Allmachtsanspruch” von TDD nicht. Im Rahmen eines durch Nachdenken entstandenen Lösungsansatzes kann TDD jedoch eine Hilfe bei der Implementierung dessen “Black Boxes” sein, d.h. der Operationen.

5. Ergebnissicherung

Das Ergebnis dieser Herangehensweise können Sie bei github einsehen. Tests und Produktionscode liegen in einem Gist. An dieser Stelle möchte ich daraus nur einen Teil zitieren, um zu zeigen, was ich unter Verständlichkeit verstehe:

image

Diesen Code kann man von oben nach unten lesen. Das Abstraktionsniveau sinkt von Methode zu Methode. Jede steht für einen Aspekt. Die Ebenen der obigen Zeichnung sind erhalten; der Code spiegelt damit den Entwurf. Oder umgekehrt: der Entwurf kann aus dem Code herausgelesen werden.

Wie funktioniert Word Wrap?

  1. In Wrap() hineinschauen und erkennen, dass dort einfach nur an eine Methode delegiert wird. Was sagt uns das? Hier ist Rekursion im Spiel. Das ist ein Pattern. Wrappen ist also eine Tätigkeit, die irgendwie mehrfach ausgeführt wird, um das Gesamtergebnis herzustellen.
  2. In Wrap_remaining_text() hineinschauen und dort die Schrittfolge sehen:
    1. Zeile extrahieren
    2. Zweile an neuen Text anhängen
    3. Das Ganze wiederholen mit dem restlichen Text [6]
  3. Und wie funktioniert das mit dem Extrahieren der Zeile? Einfach in der nächsten Methode nachschauen:
    1. Zeile einfach abschneiden vom Text
    2. Heilen eines eventuell dabei “verwundeten” Wortes

Und so weiter… Je nach Interessenlage des Lesers kann die Erkundung tiefer und tiefer vordringen. Jede einzelne Funktion ist überschaubar: der Name sagt den Zweck an, die wenigen Zeilen sind leicht zu entziffern.

Das ist grundsätzlich anders bei den im vorherigen Artikel zitierten Lösungen. Die sind monolithisch. Word Wrap findet dort irgendwie statt. Erfolgreich – jedoch wenig verständlich.

Reflexion

Ob ich “die beste” Lösung gefunden habe? Keine Ahnung. Ich habe eine ausreichend funktionierende gefunden; sie ist good enough. Und ist finde sie verständlich. Nicht nur, weil ich sie entworfen und implementiert habe, sondern weil die “Gedankenstrukturen” im Code sichtbar geblieben sind. Mehr ist mit heutigen Sprachen nicht zu wünschen, glaube ich.

Aber – so mögen Sie nun einwänden – die Lösung hat so viele LOC! Sie ist viel länger als Uncle Bobs.

Ja? Und? Warum ist das ein Problem? Macht das einen Unterschied bei der Compilation oder in der Laufzeit? Nein. Müssen wir auf Hauptspeicher oder Festplattenplatz achten? Nein.

Hat die Codierung deshalb länger gedauert? Das bezweifle ich. Aber selbst wenn, wäre mir das egal. Denn es kann nicht das Ziel der Softwareentwicklung sein, möglichst schnell Code zu schreiben, der funktioniert. Ziel muss es sein, Code zu produzieren, der lesbar und evolvierbar ist – ohne natürlich die funktionalen und nicht-funktionalen Anforderungen zu vernachlässigen.

Code wird viel öfter gelesen, als geschrieben. Deshalb sollte er fürs Lesen und nicht fürs Schreiben optimiert sein. Das habe ich getan, würde ich sagen. Das war zumindest meine Absicht.

Bonus: Nagelprobe Evolution

Ob Code clean genug ist oder nicht, kann man eigentlich nicht 100% entscheiden, indem man ihn nur liest. Seine Sauberkeit erweist sich erst, wenn man ihn verändern will. Wie geschmeidig ist er dann?

Das ist auch das Problem von 99% der Literatur. Dort Code präsentiert in eingefrorenem Zustand. Wie lange jemand dafür gebraucht hat, sieht man nicht. Wie gut die Struktur im Lichte der so wichtigen Evolvierbarkeit ist, sieht man nicht.

Also versuche ich es ein bisschen besser zu machen. Hier drei neue Anforderungen an den Code:

  1. Der umzubrechende Text besteht nicht aus einer, sondern aus mehreren Zeilen. Die können als Absätze verstanden werden.
  2. Zusammengesetzte Worte können bei Bindestrichen getrennt werden.
  3. Zeilen sollen im Blocksatz formatiert werden.

Wie genau diese Funktionalität aussieht, ist zunächst nicht wichtig. Sie zu implementieren, ist eine Fingerübung. Im Sinne der Evolvierbarkeit ist interessanter, wie leicht kann ich feststellen, wo überhaupt Veränderungen angebracht werden müssen? Werden bestehende Aspekte beeinflusst; welche? Sind neue Aspekte einzufügen; wo?

Für das alles, will ich natürlich nicht Berge an Code durchsehen müssen. Ich will auch höherer Abstraktionsebene erstmal darüber grübeln.

Blocksatz

Also: Wo müsste ich beim Blocksatz ansetzen? Muss eine Funktionseinheit, ich meine eine Methode, verändert werden? Sollte ich den Blocksatz irgendwo dazusetzen?

Blocksatz ist sicherlich ein ganz eigener Aspekt. Der hat nichts mit Texttrennung zu tun und auch nicht mit dem Zusammenbau des umgebrochenen Textes. Also darf keine bisherige Operation dafür verändert werden. Stattdessen sollte der Blocksatz zwischen den bisherigen Aspekten Textzerlegung und Textzusammenbau stattfinden. Weder muss die Textzerlegung davon wissen, noch der Textzusammenbau.

image

Der Blocksatz muss sich nur auf die gerade “abgebrochene” und “geheilte” Zeile konzentrieren. Das kann so im Code aussehen:

image

Wieder lasse ich bewusst Details weg. Weil ich es kann. Weil es überhaupt mehrere Abstraktionsebenen gibt in meinem Code. Wer sich für die genaue Implementation von Justify() interessiert, kann ja weiter “reinzoomen” im Gist.

Trennung bei Bindestrich

Wie ist es mit den Bindestrichen für die Trennung? Ist das ein neuer Aspekt wie der Blocksatz oder verändert diese Anforderung einen bestehenden?

Ich würde sagen, hier geht es um eine Variation der Operation “Schnitt korrigieren” im ersten Entwurfsbild bzw. Schritt 2 in der verbalen Beschreibung des Lösungsansatzes.

Falls ein Wort zerschnitten wurde, wird sein “Kopf” nicht einfach komplett wieder an den “Rumpf” im Resttext geheftet, sondern es wird nach einem Bindestrich im “Kopf” gesucht und nur der dahinter liegende “Teilkopf” wird angeheftet.

Hier dazu die neuen Testfälle für die “Heilung” der Zeilenabspaltung:

image

Dadurch ändert sich natürlich einiges am Arbeitspferd der “Heilung”. Dieser Aspekt bekommt Sub-Aspekte – eine Zeile kann entweder mit einem zerschnittenen Wort enden oder mit einer “Silbe”:

image

…die sich dann auch im Code niederschlagen müssen:

image

Und nochmal: Wer mehr Details sehen will, “zoomt rein”. Für ein Verständnis des Lösungsansatzes sind das nicht nötig.

Mehrzeilige Texte

Für die Behandlung mehrzeiliger Texte ist zunächst zu klären, wie der API aussehen soll. Liegen die Texte als ein string vor, in dem Zeilen durch \n abgetrennt sind? Oder liegen mehrere Texte z.B. in Form eines IEnumerable<string> vor?

Ich nehme mal an, dass es weiterhin nur ein string ist, in dem jetzt auch schon Zeilenumbrüche vorhanden sein können. Damit ändert sich der API nicht.

Diese Zeilenumbrüche sollen natürlich erhalten bleiben. Das ist in einem Testfall zu spezifizieren:

image

Der Lösungsansatz sieht dafür dann im Grunde aus wie der bisherige:

  1. Etwas von einem Rest abtrennen, nämlich den nächsten Text
  2. Das Abgetrennte verarbeiten, nämlich den bisherigen Word Wrap darauf ausführen
  3. Das Verarbeitungsergebnis an das bisherige Resultat anhängen

Graphisch wächst das Modell sozusagen nach oben. Es gibt eine neue Aspekt-Ebene oberhalb des bisherigen Word Wrap: das Word Wrap (multi) :-) Es zerfällt in das Word Wrap in drei Sub-Aspekte – von denen der letzte derselbe wie beim Word Wrap ist, der Textzusammenbau.

image

Bei soviel Ähnlichkeit liegt es nahe, die Struktur der Implementation zu kopieren. Eine Rekursion ist auch hier gleichermaßen elegant wie verständnisförderlich.

image

Wie funktioniert der Code? Wrap_multi_text() verrät es. Einfach wieder von oben nach unten lesen. Der Lösungsansatz steht dort 1:1 übersetzt.

Fazit

Tut mir leid, ich kann nicht anders als zu sagen: Das war alles ganz einfach mit ein bisschen nachdenken. Think a little, code a little.

Ich hoffe, Sie sehen das genauso: Der Code ist verständlich und evolvierbar. Dass er nicht der kürzestmögliche ist, sei zugestanden. Aber ist das wirklich sooo wichtig? Nein. An LOC zu sparen, stolz auf eine kurze-knappe Lösung zu sein, halte ich für eine premature optimization.

Nichts ist natürlich gegen Eleganz zu sagen, wenn Sie die Verständlichkeit und Evolvierbarkeit erhöht. Rekursion statt Schleife finde ich deshalb für diese Kata auch so gut wie Robert C. Martin. Aber an LOC sparen zu wollen, um vielleicht hier und da ein bisschen schneller mit dem Schreiben fertig zu werden, scheint mir wenig nachhaltig. Das ist nicht in die Zukunft gedacht, in der der Code noch viele Male gelesen und verstanden werden muss.

Zum Schluss auf den Punkt gebracht. Was habe ich getan? Ich habe ganz konsequent…

  • das SRP angewandt,
  • das Prinzip SLA angewandt,
  • möglichst die grundlegenden Semantikaspekte Integration und Operation getrennt,
  • Domänenaspekte für sich getestet.

Versuchen Sie das bei der nächsten Code Kata doch auch einmal. Aspektvolles Programmieren hilft.

Fußnoten

[1] Es täte mir leid, wenn ich damit zu pauschal urteilen würde. Wenn irgendwo die Dojo-Praxis davon deutlich abweicht, dann melde man sich. Ich werde mich dann bemühen, persönlich mal dabei zu sein und zu lernen, wie es auch anders gehen kann.

[2] Dass es inzwischen hier und da Coding Dojos oder Code Retreats gibt, die sich mit Legacy Code befassen, ist löblich. Die Frage ist nur, inwiefern dort konzeptionell an die Sache herangegangen wird. In welchem Rahmen wird dort über Pathogenese und auch Salutogenese von Software nachgedacht?

[3] Ob ein Wortumbruch statt eines Zeilenumbruchs sinnvoller wäre, lässt sich nicht entscheiden. Es gibt keinen Kontext für die Code Kata. Die Aufgabe fordert ihn jedenfalls nicht. Und wenn wir im Sinne von YAGNI auch lernen wollen, genau hinzusehen und nur zu implementieren, was wirklich gefordert ist, dann tun wir gut daran, uns an die Aufgabe zu halten – zumindest solange niemand da ist, mit dem wir über ihre Sinnhaftigkeit diskutieren können.

[4] Wer dabei auch mal etwas ausprobieren muss, soll das gern tun. Nachdenken darf durch Hilfsmittel jeder Art unterstützt werden. Bei den üblichen Code Katas sollte das jedoch eher nicht nötig sein. Die sind rein algorithmisch und sehr überschaubar. Ausprobieren im Sinne von Codieren sollte nicht nötig sein.

[5] Damit will ich nicht sagen, dass man die Lösungsentwicklung und Codierung personell trennen sollte. Auf keinen Fall! Aber wir sollten anerkennen, dass das eben zwei unterschiedliche Tätigkeiten sind, die auch unterschiedliche Kompetenzen brauchen. Deshalb sollten sie nicht pauschal vermischt werden, wie es bei TDD in der Literatur der Fall ist.

[6] Bitte beachten Sie den Gebrauch von dynamic hier. Dadurch wird der Code leserlich, ohne dass ich für den Zweck der Zusammenfassung einer Zeile mit dem verbleibenden Rest einen eigenen Typen einführen muss oder auf Tuple<> ausweiche.

Donnerstag, 8. November 2012

Was andere schon richtig machen – Qualitätsbewusstsein

imageDas ist Beatrix Biczok. Sie ist Fitness Trainerin in Hamburg. Von ihr können wir als Softwareentwickler etwas lernen.

Als Fitness Trainerin ist es ihr Aufgabe, ihren Kunden zu helfen, Ziele zu erreichen. Die möchten vielleicht kräftiger oder beweglicher werden oder sie wollen abnehmen. Beatrix leitet sie zu diesem Zweck bei körperlichen Übungen an. Ob dabei Geräte zum Einsatz kommen oder nicht, hängt vom Ziel ab.

In jedem Fall müssen die Übungen in bestimmter Weise durchgeführt werden. Es gibt also richtig oder falsch. Vom Liegestütz über Klimmzug bis zum Umgang mit der Langhantel kann man effektiv trainieren oder eben nicht. Man kann Übungen so ausführen, dass sie größtmöglichen Effekt in Bezug auf das Ziel haben. Man kann Übungen so ausführen, dass sie den Körper schonen. Gelenke sollen sich nicht abnutzen, Muskeln nicht reißen, Sehnen nicht überdehnt werden, der Kreislauf nicht überlastet werden.

Zentral für die Arbeit von Beatrix ist, dass sie gerade darauf besonders achtet. Ihr ist die Qualität der Ausführung der Übungen gerade in Bezug auf die Schonung wichtig. Die steht bei ihr über der Zielerreichung. Oder genauer: Die gibt den Rahmen für die Zielerreichung vor.

Bei ihr kann man sein Ziel nur so schnell erreichen, wie es bei körperschonender Ausführung der Übungen möglich ist. Beatrix ist natürlich kundenorientiert. Sie nimmt die Ziele ihrer Kunden absolut ernst – allerdings nicht unter allen Umständen. Sie erlaubt sich, ihren Kunden nämlich insofern reinzureden, als dass sie ihnen Grenzen aufzeigt. Ihr Qualitätsbewusstsein steuert den Fortschritt, nicht der unbedingte Wille des Kunden.

Insofern handelt sie im Sinne Heinz von Försters ethisch: Sie bemüht sich darum, die Zahl der körperlichen Optionen Ihrer Kunden zu vergrößern – und mindestens zu erhalten. Denn Raubbau an der Gesundheit durch falsche Ausführung von Übungen mag zwar kurzfristig größere Schritte in Richtung Ziel ermöglichen – langfristig jedoch verringern sich dadurch die Optionen, wenn Schäden an Bewegungsapparat oder Kreislauf entstehen. Ein Ergebnis, das viele ehemalige Spitzensportler bezeugen können.

Wo ist der Bezug zur Softwareentwicklung?

Softwareentwicklung hat auch den Zweck, ihren Kunden zu helfen, Ziele zu erreichen. Wer eine Software kauft/in Auftrag gibt, der verspricht sich davon Nutzen. Möglichst schnell, möglichst umfassend. So wie ein Fitnessjünger auch möglichst schnell und umfassend kräftiger, beweglicher, schlanker werden will.

Das ist verständlich. Solches Ziel zu erreichen, wollen und sollen Fitnesstrainer wie Softwareentwickler erreichen helfen. Nach Kräften. Aber eben nicht bedingungslos. Denn eine Forderung stellt jeder Kunden implizit immer: Hilfe soll nachhaltig sein. Sie soll seine Optionen in naher Zukunft vergrößern – aber nicht auf Kosten seiner Optionen in fernerer Zukunft. [1]

Qualität in der Ausführung in jedem Moment ist für Beatrix mithin kein Selbstzweck. Das hat nichts mit Eleganz, romantischer “Handwerkskunst” oder Prinzipienreiterei zu tun. Es ist vielmehr Ergebnis der Erkenntnis, dass unbegrenzter Eifer letztlich zu persönlicher Unzufriedenheit oder gar volkswirtschaftlichem Schaden führt.

Qualität ist keine Spinnerei, sondern ökonomisches Primat – wenn, ja, wenn die mittelfristige Zukunft Bedeutung hat.

Wenn Softwareentwicklung das Ziel einer (nicht-)funktionalen Anforderungen ins Auge fasst, da sollte sie eingedenk der Erfahrung im Fitness Bereich sein. Sie sollte auf hohe Qualität bei jedem Schritt achten, um die Zahl der Optionen ihres Kunden in der Zukunft nicht auf Kosten schneller Erfolge zu verringern. Wenn Sorgfalt dann etwas mehr Zeit brauchen sollte… dann ist das eben so. Nicht der Kunde bestimmt die innere Qualität einer Ausführung, sondern der Softwareentwickler. Nur er kann sich wirklich ein Urteil darüber erlauben. Nicht der Fitnessjünger bestimmt, ob er eine Übung gut ausgeführt hat, sondern eine Qualitätssicherungsinstanz wie Beatrix.

Lassen wir uns also nicht die Butter vom Brot nehmen in der Softwareentwicklung. Kein Laie – weder Kunde, noch Manager – soll uns sagen, wie wir Software zu entwickeln haben. Wir wollen gern helfen, ihre Ziele zu erreichen – aber die Qualität darf dabei nicht auf der Strecke bleiben. Und wenn die Auftraggeber das nicht verstehen, dann müssen wir es ihnen immer und immer wieder erklären.

Beim Körper sind die Symptome falscher Ausführung von Übungen bekannt: Muskelkater, Zerrung, Muskelriss, Entzündung, Knochenbruch, zerplatzte Adern… Oder einfacher: Schmerz ist das Resultat qualitativ minderwertiger Ausführung. [2]

Und was sind die Symptome falscher Ausführung der Softwareentwicklung? Was, wenn dort nicht bei jedem Schritt auf Qualität geachtet wird? Dann leidet die Organisation Schmerzen. Der drückt sich entweder in persönlichen Schmerzen der Mitarbeiter aus: da reicht er von Lustlosigkeit über “Dienst nach Vorschrift” und psychische/somatische Krankheit bis zum Verlassen des Unternehmens. Oder er drückt sich in Mängeln auf Systemebene aus: Zeitmangel, Geldmangel, Personalmangel.

Schmerz zeigt an, wo Kompensationsfähigkeit überschritten ist.

Der Zweck von Fitness Training ist nun aber, Kompensationsfähigkeit aufzubauen. Es tut daher gut daran, keine Schmerzen zu erzeugen. Dito die Softwareentwicklung. Daher sollte Qualität den Rahmen bei der Softwareentwicklung vorgeben, in dem andere Ziele angestrebt werden, und nicht ein nice-to-have Anhängsel sein.

Das können wir von Beatrix als Fitness Trainerin lernen.

Fußnoten

[1] Von Notsituationen, in denen ungewiss ist, ob es überhaupt eine fernere Zukunft gibt, sehe ich einmal ab. Wer sich in einer Unfallsituation durch eine “körperliche Übung” in Sicherheit bringen muss, kann und soll nicht auf deren saubere Ausführung achten. Es zählt einzig der unmittelbar maximale Effekt. Wenn dabei eine Zerrung oder Schlimmeres entsteht, die langfristig eine Einschränkung darstellt, dann ist das im Vergleich zum Schaden vernachlässigbar. Es wurde unter Umständen überhaupt eine Zukunft erschaffen, was ja eine fundamentale Erhaltung von Optionen darstellt.

Dito in der Softwareentwicklung. Hier mag es zum Beispiel bei einem Startup nötig sein, überhaupt eine Software herzustellen, um fundamental Optionen in der Zukunft zu haben. Wenn die nicht sauber ausgeführt ist, also eigentlich nicht nachhaltig, dann ist das für den Moment verzeihlich.

[2] Dass es auch Menschen gibt, die Schmerz lieben, ihn quasi als Qualitätskriterium ansehen, ist nicht zu leugnen. Jenseits einer gewissen Grenze, kann das aber als Perversion bzw. pathologisch angesehen werden. Ob wir uns dann als Befriedigungsgehilfen einspannen lassen… das muss jeder für sich überlegen. Als Normalzustand sollten wir es nicht ansehen.

Sonntag, 4. November 2012

Notorische Aspektlosigkeit

Immer wieder, wenn ich Resultate von Coding Dojos sehe, beschleicht mich ein ungutes Gefühl. Irgendetwas fehlt mir bei den Lösungen. Dabei ist es egal, ob das Coding Dojo in der Gruppe durchgeführt wurde oder sich jemand allein zum Einzeltraining ins Dojo begeben hat.

Anlässlich eines kundeninternen Coding Dojos habe ich nun nochmal länger über die gefühlte Lücke nachgedacht. Als Aufgabe wurde die Kata Word Wrap bearbeitet. Der Abend war gut vorbereitet: Laptop, Beamer, Catering, Aufgabenbeschreibung. Und sogar Testfälle waren hübsch priorisiert schon notiert, damit sich die Gruppe auf die TDD-Fingerfertigkeit konzentrieren konnte [1].

Nach knapp 90 Minuten war denn auch die Lösung fast wie aus dem Buch erarbeitet. Alles war harmonisch verlaufen. Kein Haareziehen, keine Weigerungen, mal nach vorn zu gehen und im Pair zu codieren. Alles hätte also gut sein können.

Allein… der Code sah so aus:

image

oder vielleicht auch so:

image

oder er könnte auch so ausgesehen haben:

image

Letztlich ist es egal, wie genau er ausgesehen hat. Ich habe ihn nicht mitgeschrieben, die Beispiele oben sind aus den Weiten des Internet. Alle haben aber das gemeinsam, was ich als fehlend empfunden habe.

Mir fehlen die Aspekte.

Coding Dojo Lösungen sind nach meiner Erfahrung notorisch aspektlos. Oder anders ausgedrückt: die Prinzipien SRP und SoC finden selten Anwendung. SLA kommt allerdings hin und wieder vor.

Worüber ich immer wieder gestolpert bin ist also die Unverständlichkeit der Lösungen. Wie funktioniert denn so ein Word Wrap. Schauen Sie sich einfach das erste Listing an und formulieren Sie in einem ganzen Satz, wie (!) das geht mit dem Wortumbruch.

Ich bezweifle nicht, dass die Lösungen funktionieren. So unterschiedlich sie aussehen, die kanonischen Testfälle absolvieren sie fehlerfrei. Aber das ist ja seit 50 Jahren eher nicht das Problem in der Softwareentwicklung. Wir können lauffähige Software herstellen. Und auch noch solche, die performant, skalierbar, sicher usw. ist.

Dass in der Eile da mal ein paar Bugs durch die Maschen schlüpfen… Das ist nicht schön, kriegen wir aber in den Griff, wenn wir mehr automatisiert testen. TDD hilft auch.

Aber ist der nützliche Code, den wir da produzieren auch verständlich?

Eher nicht. Und das beginnt bei so kleinen Beispielen wie den obigen.

Vielleicht stelle ich mich jetzt dumm an? Vielleicht schade ich geradezu meinem Ansehen, wenn ich meine Begriffsstutzigkeit so deutlich dokumentiere? Keine Ahnung. Ist mir auch egal. Fakt ist, dass ich es schwierig finde, aus den obigen Beispielen herauszulesen wie (!) so ein Wortumbruch funktioniert.

Vielleicht schauen Sie ja einfach drauf und sagen nach 10 Sekunden: “Achsooo! So geht das. Klar. Hätte ich auch so gemacht.” Kann sein. Dann beneide ich Sie. Ich würde das auch gern können.

Bitte verstehen Sie mich richtig: Ich kann natürlich den Code lesen und herausfinden, wie er funktioniert. Ich kann auch selbst eine Lösung herstellen.

Mir geht es aber nicht darum, ob ich irgendwie zu einem Codeverständnis gelangen kann, sondern wie schnell.

Ich habe den vielleicht verwegen klingenden Anspruch, dass sich im Code die Domäne widerspiegelt. Dass oben drüber Wrap() steht, ist mir nicht genug. Dadurch weiß ich nur, worum es irgendwie geht. Das hilft mir wenig, wenn ich den Code ändern soll, weil ein Bug drin steckt oder sich die Anforderungen geändert haben.

Sorry to say, die obigen Lösungen sind im Wesentlichen alle Monolithen. Kleine Steine, aber immer noch ziemlich strukturlose Steine. Nur Uncle Bob hat es etwas besser gemacht. Aber dazu gleich mehr.

Ich hab es einfach satt, im Steinbruch zu arbeiten. Ich will nicht mehr Archäologie betreiben und darüber rätseln, was eine Entwicklerintelligenz irgendwann mal ersonnen hat, das ich mir nun mühsam wieder erarbeiten muss. Ich will nicht bei jedem 20-Zeiler Reverseengineering betreiben. Das ist einfach teuer. Das kostet wertvolle Lebenszeit. “Was will uns der Autor sagen?” ist eine Frage für das Feuilleton oder den Deutschunterricht.

Jeder genügend obskure Code erscheint magisch ;-) (frei nach Arthur C. Clark)

Was ist stattdessen will, ist eine irgendwie lesbare Lösungsbeschreibung – im Code. Ja, genau, im Code. Mir geht es nicht um Papierdokumentationsberge und auch nicht um mehr Kommentare. Ist schon so, im Code steckt die ultimative Wahrheit. Aber wenn das so ist, dann doch bitteschön etwas leichter erkennbar als bei den Upanishaden oder der Kabbala.

Also, wie funktioniert ein Word Wrap? Keine Ahnung, wie die obigen Autoren meinen, dass es funktioniert. Aber hier mal ein Vorschlag:

  1. Der Input-Text wird in Worte zerlegt.
  2. Die Worte werden in Silben zerlegt.
  3. Die Zeilen des Output-Textes werden aus Silben zusammengesetzt, bis die maximale Zeilenlänge erreicht ist.

Das ist mein Vorschlag für eine Lösung auf hohem Abstraktionsniveau, sozusagen ein Entwurf. Ich habe das Problem in drei Aspekte zerlegt: Wortermittlung, Silbenbestimmung, Zeilenzusammenbau. Der Entwurf folgt damit meiner Interpretation der Aufgabenstellung, die da lautet “KataWordWrap”.

  1. Ich nehme den Wortumbruch ernst. Deshalb taucht in meiner Lösungsskizze der Begriff “Wort” auf.
  2. Die Silben sind meine Hinzudichtung. Die stehen nicht in der Aufgabenstellung. Mit ihnen meine ich auch nicht un-be-dingt Sil-ben, sondern Abschnitte von Worten, zwischen denen getrennt werden darf. In der Problemstellung ist das jeder Buchstabe. Den Begriff “Silbe” habe ich hinzugedichtet, um das Problem an etwas Bekanntes anzuschließen.

Mit dieser Lösungsidee im Hinterkopf schaue ich nun auf den obigen Code und sehe… nichts. Dort tauchen die Begriffe “Wort” oder “Silbe” nicht auf. Und Lösungsschritte sehe ich auch nicht.

Allerdings macht Uncle Bob eine Ausnahme. Dessen Code liefert einen gewissen Hinweis auf seinen Lösungsansatz. Allerdings lese ich auch da nichts von “Wort” oder “Silbe”, sondern von “Zeilenbruch”. Hm… das ist interessant.

Meine Interpretation: Uncle Bob löst ein anderes Problem als ich ;-) Uncle Bob löst das Problem “Zeilenlängenbegrenzung” (oder so ähnlich) und nicht das Problem “Wortumbruch”. Beides mag ähnlich klingen, ist aber etwas anderes.

Wer hat nun Recht? Im Ergebnis würden wir wohl dasselbe liefern – aber unsere Lösungen würden sich eben unterscheiden. Wir sehen unterschiedliche Domänen.

Aber auch wenn Uncle Bob eher die Domäne im Code repräsentiert, wie er sie aus der Aufgabenstellung herausgelesen hat, finde ich seinen Lösungsansatz noch nicht leicht zu lesen. Was bedeutet zum Beispiel if (s.charAt(col) == ‘ ‘)?

Klar, dass kann ich mir irgendwie zusammenreimen. Aber ich will mir nichts mehr zusammenreimen. Ich habe mal Informatik studiert und nicht Ethnologie oder Archäologie. Wenn der Code nicht intention revealing ist, dann habe ich damit ein Problem.

Code muss ich lesen und verstehen, nicht wenn und weil er läuft, sondern wenn er eben nicht läuft wie gewünscht, d.h. wenn ich an ihm etwas verändern soll. Dann muss ich verstehen, wie er seine Aufgabe heute erledigt und leicht die Ansatzpunkte identifizieren, wo ich Veränderungen im Sinne neuer Anforderungen anbringen muss.

Was könnten denn Veränderungen sein, die an “Word Wrap” Code herangetragen werden?

  • Die maximale Output-Zeilenlänge ändert sich. Es verändert sich nur eine Konstante.
  • Der umzubrechende Text liegt nicht in einer Zeile, sondern in mehreren vor. Es verändert sich also die Struktur der Quelldaten.
  • Der Quelltext hat mehrere Absätze, die im Output erhalten bleiben sollen. Eine Veränderung nicht nur in der Quelldatenstruktur, sondern auch in der Zieldatenstruktur.
  • Worte sollen nicht mehr an beliebiger Stelle geteilt werden dürfen, sondern nur noch zwischen Silben.
  • Beim Umbruch sind Satzzeichen zu berücksichtigen. So darf zum Beispiel ein Bindestrich nicht am Anfang einer neuen Zeile stehen.

Ich behaupte nun nicht, dass eine Kata-Lösung all dies und mehr vorhersehen sollte. Keinesfalls! Die Lösung soll ausschließlich den aktuellen Anforderungen genügen. Auch wenn meine Lösungsskizze oben Silben erwähnt, würde ich also keine echte Silbenerkennung implementieren, sondern den Umbruch bei jedem Buchstaben zulassen.

Dennoch ist ja eines immer wieder so überraschend wie Weihnachten: dass sich Software über die Zeit ändert.

Deshalb sollten wir Vorkehrungen treffen, damit uns das leicht fällt.

Für mich bestehen solche Vorkehrungen mindestens darin, die Aspekte des Lösungsansatzes klar im Code sichtbar zu machen. Und genau das geschieht eben nicht in den ersten beiden obigen Lösungen und nur bedingt bei Uncle Bob.

Wir können Aspekte in Methoden oder Klassen verpacken. Je nach Umfang. Das ist doch kein Hexenwerk. Und wir können auch noch die fundamentalen Aspekte Integration und Operation trennen.

Dann würden unsere Lösungen lesbarer, glaube ich.

TDD is not enough

Noch ein Wort zur Ursache der Unlesbarkeit. TDD ist da sicherlich nicht schuld. TDD ist ne gute Sache. Soll man im Coding Dojo gerne üben.

Aber man sollte eben die Heilsbringererwartungen nicht zu hoch schrauben. TDD dreht sich am Ende doch “nur” ums Testen. Auch wenn es red – green – refactor heißt, so führt das eben nicht automatisch zu lesbarem, evolvierbarem Code.

Eine gut priorisierte Liste von Testfällen plus red – green liefert Code, der in kleinen Schritten hin zu einer funktionalen Lösung wächst. Die Codeabdeckung wird groß sein. Das halte ich für das erwartbare Ergebnis, sozusagen das kleinste garantierte Resultat von TDD.

Der Rest jedoch… der hat eben nichts mit TDD zu tun. Verständlichkeit, Evolvierbarkeit, punktgenaues Testen (also Unit Tests statt Integrationstests [2])… all das ist optional bei TDD. Es entsteht nicht zwingend. Das ist meine Erfahrung aus vielen Coding Dojos und zeigt sich auch an den obigen Codebeispielen.

Damit behaupte ich nicht, dass das nie entstehen würde. Ich sage nur, das entsteht nicht durch TDD, sondern durch Refactoring-Mühe und –Kompetenz.

Refactoring mit TDD gleichzusetzen, wäre jedoch falsch. Refactoring ist eine unabhängige Disziplin. Deshalb sage ich, Verständlichkeit und Evolvierbarkeit entstehen eben nicht durch TDD. Und wenn man in einem Coding Dojo TDD übt, dann übt man de facto eben eher nicht Refactoring. Das wäre auch eine Überlastung für die Beteiligten, ein Verstoß gegen das SRP, sozusagen. Mit der Definition von Tests, mit dem red-green-Rhythmus, mit KISS bei der Implementation, mit all dem haben die Beteiligten schon genug Mühe. Wenn Refactoring da nicht quasi im Stammhirn verankert ist, also schon als Reflex existiert, dann findet Refactoring nur auf Alibiebene statt, da wo es einem schlicht ins Auge springt, weil so deutliche Anti-Muster im Code zu sehen sind.

Was dabei aber eher nicht entsteht, das ist Aspekttrennung. Und schon gar nicht entstehen getrennte Tests für Aspekte.

Indem ich das so “anprangere” will ich nicht die Praxis der Coding Dojos herabsetzen. Mir geht es nur um realistischere Erwartungen. Die Ergebnisse sind schlicht und einfach Ergebnisse der grundlegenden Wahrheit: you always get what you measure. Denn was bei TDD “gemessen” wird, worauf das Augenmerk liegt, das sind die Tests. Es heißt ja “Test-Driven Development” und nicht “Design-by Testing” oder so. Tests stehen im Vordergrund und im Mittelpunkt. Und die kriegt man. Alle sind zufrieden, wenn sie im red-green-Rhythmus arbeiten und schrittweise die Lösung hinkriegen. Und auf dem nächsten Level dann noch bei jedem green in ein VCS einchecken.

Das ist alles super. Nur eben auch nicht mehr. Es kommen Lösungen heraus, die testabgedeckt sind. Wunderbar. Man merkt ihnen an, dass sie stolz geschrieben wurden. Ans Lesen und spätere Verändern wurde dabei wenig Gedanke verschwendet. Während des Coding Dojos ist ja der ganze Kontext auch in allen Köpfen, die Lösung entsteht vor aller Augen. Niemand hat ein Problem mit der Verständlichkeit. Noch nicht.

Deshalb erhärtet sich bei mir weiter der Verdacht: TDD is not enough. Wenn wir das sehr löbliche Üben auf TDD beschränken, dann tun wir zuwenig.

Dass es darauf beschränkt ist, finde ich ja verständlich. red – green + KISS ist so schön greifbar. Da sieht man den Erfolg. Und KISS ist ganz im Sinne des alten Eleganzgedankens.

Leider sind das Ergebnis dann lokale Optima. Knapper testabgedeckter funktionaler Code ist im Sinne dieses Vorgehens optimiert – aber hat nicht das Big Picture im Blick. Zu dem gehören für mich Verständlichkeit und Evolvierbarkeit.

In diesem Sinne ist auch die Freude aller zu verstehen, wenn die Lösung kleiner wird. Weniger LOC sind gut. Als sei darauf ein Preis ausgelobt.

Es gilt jedoch nicht Verständlichkeit == Kürze. Verständlichkeit mag mit Kürze korrelieren, deshalb ist jedoch der kürzeste Code nicht automatisch der verständlichste. Für mich darf also etwas “Fluff” in einer Lösung sein, gar etwas Redundanz, wenn dadurch Verständlichkeit und Evolvierbarkeit steigen.

Versuchen Sie es doch mal. Einfach so als Experiment. Beim nächsten Coding Dojo könnten Sie sich bewusst mal die Frage stellen, welche Aspekte Sie erkennen – und dann überlegen Sie, wie Sie die in der Lösung deutlich sichtbar machen. Darauf aufbauen könnten Sie nach einer Formulierung der Lösung suchen, die ihre Funktionsweise leicht erkennbar macht. Wie arbeiten die Aspekte einer Lösung zusammen, um die Anforderungen zu erfüllen?

Fußnoten

[1] Dass Testfälle schon ausgearbeitet vorlagen, fand ich allerdings nicht so schön. Das schien mir dem Lerneffekt eher abträglich. Denn darin liegt ja eine der zentralen Herausforderungen bei TDD: relevante Testfälle überhaupt erst einmal erkennen und dann noch in eine Reihenfolge bringen, die ein kleinschrittiges Wachstum des Codes begünstigt.

[2] Selbst bei Uncle Bob gibt es nur Tests für wrap(). Die herausgelöste Funktion breakline() wird nicht separat getestet, obwohl sie ein Arbeitspferd der Lösung darstellt. Damit sind alle Tests seiner Lösung Integrationstests – auch wenn sie auf einem höheren Abstraktionsniveau die Unit wrap() testen.