Donnerstag, 13. August 2020

Sei kein Held, sei eine Null! Über 10x Developer und andere Herausforderungen.

Im November 2018 habe ich bei den XP-Days in Hamburg meine erste (und bisher einzige) "eXtreme Presentation" (manchmal auch Pecha Kucha genannt) gehalten. Dabei werden in 6'40" genau 20 Folien jeweils genau 20 Sekunden lang gezeigt und dann automatisch weitergeschaltet. Beim Aufräumen meiner Festplatte habe ich meinen Text zu den Folien wiedergefunden. Und da es meines Wissens weder Fotos noch einen Video-Mitschnitt von der Präsentation gibt, halte ich die Kurzgeschichte einfach hier für die "Ewigkeit" fest :-)

[Update 04.03.2021] Bei der OOP 2021 durfte ich diesen Pecha Kucha noch einmal halten. Den Video-Mitschnitt davon gibt es hier.

[0:00, "Kleine Helden"]
Moin zusammen!
Wollen wir nicht alle kleine oder große Helden sein? Zumindest ein klein bisschen?
Die meisten von uns wollten als Kind vermutlich Superkräfte haben. Besser sein als die anderen. Herausstechen. Bewundert werden.
Aber es ist doch viel schöner, einen Freund zu haben, der auch Superkräfte hat. Dann bin ich nicht so allein.

[0:20, "Berggipfel"]
Später im Leben nehmen wir uns dann richtig große Ziele vor. Wir suchen Herausforderungen.
Wir wollen hoch hinaus, vielleicht ganz nach oben.
Und das ist auch gut so. Denn ohne das Streben nach Besserem hören wir irgendwann auf, gut zu sein.
Aber: Wir merken schnell, dass wir es nach oben nicht alleine schaffen.

[0:40, "Seilschaft"]
Es mag Einzelne geben, die riskieren alles und gewinnen alles alleine. Der Großteil der Einzelnen schafft das nicht. Die gehen unter. Die stürzen ab.
Zusammen sichern wir uns – Profis und Neulinge – nicht nur dort, wo es steil ist, sondern auch schon vorher, weil wir die unsicheren Stellen nicht sofort als solche erkennen.
Und dann gelangen wir zusammen nach oben. Und stehen über der Welt.

[1:00, "ISS"]
Am höchsten hinaus geht’s zum ultimativen Abenteuer unserer Zeit. In den Weltraum, zur ISS – hier noch mit Space Shuttle aus der Zeit des Aufbaus.
Wer mich kennt, weiß, dass Weltraumfahrt mein Hobby ist.
Ich bin leider kein Astronaut geworden, begeistere mich aber trotzdem auf viele Arten dafür...

[1:20, "LEGO-Apollo"]
Hier seht Ihr beispielsweise mein Weihnachtsgeschenk vom letzten Jahr.
Mein Sohn hat natürlich angeboten, mitzuhelfen. Ich durfte dann geduldig beobachten und bei den schwierigen Stellen ab und zu auch mitmachen.
Wenn wir nicht gerade LEGO bauen, lese ich gerne Bücher über die Raumfahrt. Auf ein Buch wurde ich im vergangenen Jahr durch einen Vortrag von Jens Schauder aufmerksam.

[1:40, "Chris Hadfield"]
Es geht um das Buch "An Astronaut’s Guide to Life on Earth" von Chris Hadfield, einem Kanadier.
Er flog mir dem Space Shuttle zur Mir, zur ISS, und war später 6 Monate auf der ISS.
Bekannt wurde er den meisten wohl durch seine Cover-Songs aus dem All, hier z.B. „Space Oddity“ von David Bowie.
(Zu finden auf YouTube; und ja, er hat auch diverse populärwissenschaftliche Berichte aus dem All gesendet ;-)

[2:00, "Aim to be a Zero"]
In dem Buch prägte er den Satz „Aim to be a Zero“.
Er teilt Astronauten in drei Kategorien ein:
Die "+1" sind ein echter Gewinn fürs Projekt und fürs Team. Das Problem dabei: Wer es darauf anlegt, eine +1 zu sein, steuert geradewegs auf eine -1 zu. Weil er als Angeber rüberkommt. Oder weil er seine Aufgabe grandios verbockt.

[2:20, "Space Shuttle Cockpit"]
Nun sind Astronauten alle Überflieger, die Besten der Besten, die wollen alle eine +1 sein. Trotzdem müssen gerade sie sich bemühen, einfach nur ihre Arbeit gut und unaufgeregt zu machen. Einfach eine gute Null zu sein.
Denn der Versuch, alles über-perfekt zu machen, führt in diesem Umfeld schnell in die Katastrophe...

[2:40, "John Glenn"]
Astronauten müssen lernen, sich zurückzunehmen, obwohl sie natürlich ALLES können.
Das ist schwierig und war auch nicht immer so. Die ersten Raumkapseln wurden alleine geflogen, erst danach tauchten Teams in den Raumschiffen auf.
Die Astronauten der ersten Generation waren Cowboys, Draufgänger.
Einzelkämpfer mit ausgeprägtem Ego.

[3:00, "10x Developer"]
Das kennen wir auch in der Softwareentwicklung.
Wir kennen alle einen Kollegen, der unglaublich schnell abliefert. Der alle schwierigen Aufgaben bekommt oder sich nimmt. Der Liebling des Chefs.
Er wirkt immer beschäftigt und ist gefühlt 10x schneller als der Rest.
Deshalb nennen wir ihn „10x Developer“. Es gibt dabei aber ein Problem.

[3:20, "gut gemeint"]
Wenn ich glaube, der Einzige zu sein, der solche Probleme lösen kann, dann werde ich das auch irgendwann sein. Dann werde ich zum Flaschenhals meines Teams und meiner Firma.
Und nach dem Abliefern der schnellen Lösung muss das Team 10x so lange aufräumen, bis die Lösung stabil betrieben und weiterentwickelt werden kann...

[3:40, "ISS-Schlaf"]
Außerdem entsteht durch solche 10x Developer eine Heldenkultur, in der Feuerwehreinsätze belohnt und nachhaltige Lösungen vermieden werden. Das führt irgendwann dazu, dass sich ein Teil der Kollegen in Bürokratie und Prozess versteckt, während der andere Teil sich selbst ausbeutet und dabei ausbrennt. Schlafmangel ist nur ein Symptom davon.
Astronauten bekommen übrigens genug Schlaf, darauf achtet die Bodenkontrolle.

[4:00, "10x besser"]
10x Developer kann man auch anders definieren, wie Kate Heddleston das in ihrem Blog tut:
Mach die Menschen um dich herum 10x besser!
Du bist nicht gut, wenn Du besser bist als die anderen, sondern wenn Du den anderen hilfst, genauso gut wie Du zu werden.
Das nützt dem Team, dem Projekt, der Firma langfristig.

[4:20, "Außenbordeinsatz"]
Wie passt das zu den Astronauten? Immerhin sehen wir immer solche heldenhaften Fotos...
Was man hier aber nicht sieht: Da draußen ist noch mindestens ein zweiter Astronaut, damit sie sich gegenseitig helfen können. Und drinnen noch ein Dritter als Unterstützung.
Die Astronauten MÜSSEN bei solchen Aufgaben eine unaufgeregte, gute Null sein.

[4:40, "Alexander Gerst (Buch)"]
Und das mit der Null ist keine Privatmeinung von Chris Hadfield. Alexander Gerst erwähnt das ebenfalls als mittlerweile normale Erwartungshaltung. Es geht dabei nicht um Mittelmäßigkeit, sondern um „gut genug“. Denn perfekt wird es sowieso nie. Und bei komplexen Aufgaben können Aktionismus und Heldentum katastrophale Folgen haben.
Warum ist das für uns in der Softwareentwicklung relevant? Viele unserer Probleme sind unglaublich komplex.

[5:00, "Cynefin"]
Das Cynefin-Framework (Kai-ne-vin) teilt unsere Probleme in vier Kategorien ein. In der einfachen oder offensichtlichen Kategorie und vielleicht auch noch in der komplizierten Kategorie können wir Probleme vielleicht noch als Einzelkämpfer lösen.
Die Probleme der komplexen Kategorie aber können wir erst im Nachhinein richtig verstehen. Das ist in den meisten Fällen viel zu komplex für einen Kopf allein. Diese Probleme sollten wir mit dem ganzen Team lösen.

[5:20, "Star Shape"]
Dafür müssen wir uns als Entwickler anders aufstellen. Vielleicht habt Ihr vom T-Shape gehört: Zusätzlich zu meinem technischen Spezialwissen schaue ich über den technischen Tellerrand. Aber auch das reicht nicht aus. Ich muss das Problem auch fachlich verstehen. Und ich muss soziales Können haben, um mich mit anderen auszutauschen.
Das nennen wir Star-Shape. Star-shaped developers, star-shaped testers, star-shaped product owners...

[5:40, "Mob Programming"]
Und vielleicht benötigen wir dafür auch andere Formen der Zusammenarbeit.
Aufgaben einzeln lösen und das Ergebnis reviewen, wird den immer komplexeren Problemen nicht mehr gerechnet. Vielleicht ist selbst Pair Programming nicht genug, wenn nämlich das gesamte Team den Lösungs-Prozess verstehen muss.
Mob Programming, bei dem das gesamte Team zusammen eine Aufgabe löst, hat sich hier als unglaublich hilfreich erwiesen.

[6:00, "Sharing Culture"]
Das alles wird letztlich auch die Kultur unserer Zusammenarbeit und die Kultur unseres Unternehmens verändern.
Wir brauchen keine geteilten Dokumente. Sondern geteiltes Verstehen durch gemeinsam Machen.
Weg von einer Kultur des Versteckens, Abkapselns – hin zu einer Kultur des Nehmens und Gebens von Wissen und Können.

[6:20, "Danke"]
Sei kein Held. Leg es nicht darauf an, hervorzustechen.
Versuch lieber, eine gute Null zu sein. Mit all Deinem Können. Hilf dem Team. Lass Dir helfen.
Werdet zusammen besser.
Dann löst Ihr gemeinsam und heldenhaft die immer komplexeren Probleme unserer Zeit.
Sei kein Held, sei eine Null. Aim to be a Zero.
Vielen Dank!

[6:40, Ende]


Von Benjamin Felis gibt es übrigens ein ganz tolles Graphic Recording aller vier Kurzpräsentationen.

Montag, 11. Dezember 2017

Unendliche Streams in Java 8 und 9

[This text is available in English on DZone.]

Mit Lambdas und Streams können wir seit Java 8 endlich etwas funktionaler programmieren. Ein wichtiges Konzept in funktionalen Sprachen sind unendliche Streams, die durch geeignete Bedingungen passend "abgebrochen" werden (die Streams sind natürlich nicht wirklich unendlich, sondern sie werden "lazy" - also erst bei Bedarf - ausgewertet). So etwas gibt es auch in Java, und das möchte ich mit dem folgenden Beispiel zeigen.

Es geht um den Luhn-Algorithmus zur einfachen Berechnung einer Prüfsumme. Inspiriert dazu wurde ich durch den JVM Functional Language Battle von Falk Sippach. Zu sehen gab es in dem Vortrag verschiedene Implementierungen, von klassischem, imperativem Java bis hin zu Scala, Frege und Java mit Vavr (vormals Javaslang).

Eine kurze Zusammenfassung vom Luhn-Algorithmus:
  • Gegeben sei eine nicht negative, ganze Zahl beliebiger Länge.
  • Von rechts nach links wird jede 2. Ziffer der Zahl verdoppelt. Entsteht dabei eine zweistellige Zahl, wird die Quersumme gebildet.
  • Alle so entstandenen Ziffern (einfach und verdoppelt) werden aufsummiert.
  • Die Prüfsumme ist gültig, wenn die Gesamtsumme ohne Rest durch 10 teilbar ist.
Wie kann man das mit Java-8-Streams implementieren? Dazu noch ohne Variablen, Zustandsänderungen und Fallunterscheidung? Beispielsweise so:

import java.util.PrimitiveIterator;
import java.util.stream.IntStream;

public class Luhn {

  public static boolean isValid(String number) {

    PrimitiveIterator.OfInt faktor =
      IntStream.iterate(1, i -> 3 - i).iterator();

    int summe = new StringBuilder(number).reverse()
      .toString().chars()
      .map(c -> c - '0')
      .map(i -> i * faktor.nextInt())
      .reduce(0, (a, b) -> a + b / 10 + b % 10);

    return (summe % 10) == 0;
  }
}

Als Beispiel durchlaufen wir isValid() mit dem String "8763".

Mit IntStream.iterate() erzeugen wir uns einen endlosen (aber lazy berechneten) Stream der Zahlen 1,2,1,2,1,2, ... – das ist der Faktor, mit dem jede Ziffer multipliziert wird.

Dann packen wir den Zahlen-String in einen StringBuilder, weil der eine reverse()-Methode bietet. Damit können wir die einzelnen Ziffern als chars()-Stream vorwärts durchlaufen (und müssen nicht von hinten zählen):

'3', '6', '7', '8'

Jedes Ziffern-char wir nun mit dem ersten map() auf den Ziffern-Wert 0..9 abgebildet und mit dem zweiten map() mit dem nächsten Faktor aus dem endlosen IntStream multipliziert:

3, 12, 7, 16

Mit reduce() berechnen wir nun die Quersumme aller dieser Zahlen, wobei wir für jede Zahl die Zehnerstelle (div 10) und die Einerstelle (mod 10) addieren, also

(0 + 3) + (1 + 2) + (0 + 7) + (1 + 6)

Ergibt 20, und das ist ohne Rest durch 10 teilbar. Somit ist die Prüfsumme gültig.


Mit Java 9 können wir uns das Umwandeln in einen String (und das Umkehren der Zeichenfolge) sparen, indem wir aus der zu prüfenden Zahl einen weiteren unendlichen Stream machen, den wir mit takeWhile() und einem geeigneten Lambda-Prädikat kappen:

import java.util.PrimitiveIterator;
import java.util.stream.IntStream;
import java.util.stream.LongStream;

public class Luhn9 {

  public static boolean isValid(long number) {

    PrimitiveIterator.OfInt faktor =
      IntStream.iterate(1, i -> 3 - i).iterator();

    long summe = LongStream.iterate(number, n -> n / 10)
      .takeWhile(n -> n > 0)
      .map(n -> n % 10)
      .map(i -> i * faktor.nextInt())
      .reduce(0, (a, b) -> a + b / 10 + b % 10);

    return (summe % 10) == 0;
  }
} 

Der chars()-Stream in der Java-8-Lösung war nach der Verarbeitung aller Zeichen zu Ende. LongStream.iterate() in dieser Java-9-Variante teilt die zu prüfende Zahl dagegen endlos immer weiter durch 10, also z.B.

8763, 876, 87, 8, 0, 0, 0, ...

Wir müssen den Stream also nur verarbeiten, solange die Zahl größer Null ist. takeWhile() macht genau das und liefert folgenden endlichen Stream:

8763, 876, 87, 8

Das erste map() liefert einen Stream der letzten Ziffern (mod 10):

3, 6, 7, 8

Der Rest (Multiplizieren mit dem Faktor und Quersummen-Addition) ist dann wieder wie bei der Java-8-Lösung.

Das ist vielleicht noch nicht perfekt funktional, weil wir keine Funktionskomposition beliebiger passender Funktionen durchführen können, sondern auf die vorhandene Stream-API angewiesen sind. Aber für Java-Bordmittel ist das doch gar nicht so schlecht.


Und wer noch nicht genug davon hat: Ein anderes schönes Beispiel für unendliche Java-Streams ist die Berechnung von Kaprekar-Zahlen, was kürzlich als Challenge auf dev.to lief. Neben takeWhile() kommt bei der Java-Lösung noch limit() zum Einsatz, um einen unendlichen Stream nicht mit einer Bedingung, sondern mit einer festen Anzahl zu begrenzen.

Montag, 13. März 2017

JSF-FacesContext in Unit-Tests mocken

Beim Schreiben von Unit-Tests für JSF-Beans kommt es leider ab und zu vor, dass der zu testende Code eine Fremdbibliothek verwendet, in der statisch auf
FacesContext.getCurrentInstance()
zugegriffen wird. Beim Ausführen des Tests kann dann eine NullPointerException im Code der Fremdbibliothek auftreten, die man nicht ohne Weiteres weg bekommt, denn der FacesContext hat zwar einen Getter, aber keinen Setter für das aktuelle Kontext-Objekt.

Hier kommt OmniFaces zu Hilfe – eine nützliche Hilfsbibliothek für JSF, die man vielleicht sowieso schon ins Projekt eingebunden hat. OmniFaces bietet die Methode Faces.setContext() an, mit der man den aktuellen JSF-Kontext setzen kann. Normalerweise benötigt man diese Methode nicht, aber in JSF-Unit-Tests kann sie Gold wert sein. Übergibt man z.B. ein Mockito-Mock-Objekt, ist man die NullPointerException schnell und einfach los:
import org.mockito.Mock;
import org.omnifaces.util.Faces;

public class FrontendBeanTest {

    @Mock
    private FacesContext facesContextMock;

    @Test
    public testFuerJsfBean {

        // Given
        Faces.setContext( facesContextMock );

        // When
        ...

        // Then
        ...
    }
}

Freitag, 10. März 2017

Firefox 52 und Java-Applets

Mit Version 52.0 hat Firefox die Unterstützung für Plugins eingestellt, die auf die Uralte NPAPI setzen – das betrifft auch das Java-Plugin und somit Java-Applets.

Das war schon länger angekündigt und ist absolut nachvollziehbar, denn die NPAPI hat diverse Sicherheitsprobleme. Zudem sind Java-Applets eine sterbende Technologie, und mit Java 9 wird das Applet-Browser-Plugin auf den Status deprecated gesetzt werden.

Manchmal ist man aber doch noch darauf angewiesen, dass man ein Applet im Browser aufrufen kann. Und das geht auch mit Firefox 52 noch:
  • Zum Einen kann man Firefox 52 ESR (Extended Support Release) verwenden. Darin wird der Plugin-Support bis Mai 2018 aktiviert bleiben.
  • Zum Anderen – und das ist vermutlich die schnellere Lösung – kann man die Firefox-Konfiguration anpassen. Dazu ruft man in der Adresszeile about:config auf, macht einen Rechtsklick in die Liste der Optionen und wählt im erscheinenden Popup Neu/Boolean auf. Als Namen gibt man plugin.load_flash_only an, als Wert false. Nach einem Browser-Neustart können Java-Applets dann wieder genutzt werden.
Und immer dran denken: Die Nutzung sowohl dieser Informationen als auch von Plugins wie Flash und Java-Applets geschieht vollkommen auf eigene Gefahr.

Mittwoch, 22. Juni 2016

Pair Programming & das Gehirn

Mitte Mai haben Stephan Kraus und ich auf der SEACON einen Vortrag über Pair-Programming-Coaching im Großprojekt gehalten. Wir haben dabei von unseren Erfahrungen mit der Neu- und Weiterentwicklung einer großen E-Commerce-Plattform berichtet.

Das Coaching bieten wir dort als Angebot an die zahlreichen Teams an, um Pair Programming einfach mal ein paar Wochen während der ganz normalen Arbeit kennenzulernen (bzw. zu intensivieren), um die Vorzüge von kontinuierlichem Wissenstransfer und gegenseitiger Qualitätsverbesserung zu erleben. Immer im Wissen, dass man auch bei schon bei ziemlich guter Qualität immer noch besser werden kann – weil wir als Menschen Fehler machen (aus denen wir als Team viel lernen können) und weil auch Technik nicht perfekt ist.

Spannend war, dass bei der Konferenz direkt nach uns der Vortrag von Julia Dellnitz und Jan Gentsch über Brain Patterns in der Softwareentwicklung stattfand – wo ganz nebenbei ;-) erklärt wurde, warum man mit Pair Programming die typischen Gehirn-Fallen, in die man sonst leicht bei der Kopfarbeit hineintappt, elegant austrickst:
  • Unser Gehirn interpretiert unklare Dinge so, wie sie uns gerade passen. Natürliche Sprache (zwischen Fachbereich und Entwickler, aber auch zwischen den Entwicklern selbst!) ist oft nicht eindeutig... Pair Programming hilft hier, indem das, was man meint, zügig als Programmcode hingeschrieben und dann vom Pair verbessert bzw. vervollständigt wird. Programmierer können erfahrungsgemäß über und mit Code viel klarer diskutieren.
  • Wir können alleine nicht gleichzeitig über das "Was" und das "Wie" nachdenken. Die zwei Rollen beim Pair Programming decken aber beides ab: Der Driver an der Tastatur kümmert sich um die Implementierung (das "Wie"), der Navigator als strategischer Denker ist für das "Was" zuständig. Und weil die Rollen in Mikroiterationen ständig wechseln, kann dann jeder doch beides zur Lösung beitragen.
Die Patterns erklären außerdem, warum es wichtig ist, Pair Programming als sehr intensive Form der Zusammenarbeit zu üben:
  • Sozialer Schmerz durch offenes, ehrliches Feedback wird im Gehirn wie physischer Schmerz empfunden. Das Geben und Annehmen von konstruktivem Feedback muss daher Schritt für Schritt gelernt und verbessert werden, damit es von den Team-Mitgliedern nicht als persönlicher Angriff empfunden wird.
  • Ich als der beste Programmierer der Welt muss lernen, dass ich vielleicht doch mal einen Fehler mache... und dass meine Kollegen manchmal auch echt gute Ideen haben.
Der Vortrag war eine schöne Bestätigung, dass wir mit der Förderung von Pair Programming als wichtiger XP-Praktik auf dem richtigen Weg sind. Die kurze, schriftliche Zusammenfassung inkl. Brain-O-Mat sollte jeder Kopfarbeiter mal gelesen haben!

Samstag, 19. März 2016

Closures in Clojure

Eine Sprache, die nach Closures benannt ist (und genauso ausgesprochen wird), sollte ziemlich gut mit Closures umgehen können :-). Das Zähler-Beispiel für Java, Groovy, Scala etc. sieht in Clojure wie folgt aus:

; closures.clj

(defn zaehlerMitStartwert [startwert]
  (let [zaehler (atom startwert)]
    (fn [] (swap! zaehler inc))))

(def next1 (zaehlerMitStartwert 10))
(def next2 (zaehlerMitStartwert 20))

(println (next1))  ;; 11
(println (next2))  ;; 21
(println (next2))  ;; 22
(println (next1))  ;; 12

In der Funktion zaehlerMitStartwert wird im let-Block ein lokales Binding für zaehler angelegt. Der Wert von zaehler ist ein Atom mit dem übergebenen startwert. Ein Atom speichert in Clojure einen Zustand, der threadsicher ausgetauscht werden kann. Rückgabe von zaehlerMitStartwert ist eine Funktion ohne Parameter: (fn [] ...) - dies ist die Closure, die die freie Variable zaehler außerhalb der Closure-Funktion bindet (umschließt). Die Closure-Funktion verändert (swap!t) den zaehler-Wert mithilfe der inc-Funktion und gibt den neuen (reingeswap!ten) Wert zurück.

Dann definieren wir zwei globale Variablen next1 und next2, die mit je einer Closure-Instanz initialisiert werden. Diese Funktionen werden mit der Clojure- bzw. Lisp-typischen Syntax (next1) aufgerufen (statt next1() o.ä.).

Mittwoch, 16. März 2016

Closures in Groovy

Beim JavaLand 2016 vor einer Woche hat Dierk König einen schönen Einstieg in die elegante und kompakte Ausdrucksweise von Groovy gegeben. Dabei hat er als Closure-Beispiel einen Zähler verwendet, den ich hier – weil er so gut zu meinen bisherigen Closure-Beispielen (u.a. für Java, JavaScript und Scala) passt – zeigen möchte (Fehler im Code gehen selbstverständlich auf mein Konto ;-):

// closures.groovy

Closure zaehlerMitStartwert(startwert) {
  return {
    startwert++
  }
}

def next1 = zaehlerMitStartwert(10)
def next2 = zaehlerMitStartwert(20)

println next1()  // 10
println next2()  // 20
println next2()  // 21
println next1()  // 11

Das return in der Funktion zaehlerMitStartwert gibt die Closure – den darauffolgenden Block mit geschweiften Klammern und der Zähl-Anweisung – zurück. Im Block selbst ist kein return notwendig, weil hier für den Compiler klar ist, dass die letzte (und hier einzige) Anweisung im Block das Ergebnis der Block-Funktion ist. Man sieht sehr schön, dass Groovy-Closures problemlos freie Variablen außerhalb des Closure-Blocks binden - so, wie das auch bei Scala der Fall ist (aber anders als bei Java, wo normalerweise nur final-Werte und nur mit Tricks Variablen gebunden werden können).

Wer das Groovy-Skript ausprobieren möchte, kann es einfach in die Groovy-Web-Console kopieren und dort ausführen.