Das ist doch alles Murcs

Manche Teams bekommen Scrum einfach nicht hin. Je nachdem wie verzweifelt der Beobachter ist, wenn er dem Treiben zuschaut, nennt er es Scrum-But, Anti-Scrum oder Zombie Scrum.
Wenn sich alles umkehrt, der PO die Kommunikation im Team organisiert, der Scrum Master passiv in der Ecke steht, die Team Mitglieder kein Interesse daran finden, funktionsfähige Software zu liefern, wenn das Daily dazu dient die Teams vorzuführen statt sich zu organisieren, dann sollte man auch den Namen einfach umkehren.
Liest man Scrum von hinten, dann liest es sich Murcs und klingt wie das schöne deutsche Wort Murks!
Murks /Múrks/

Substantiv, maskulin [der]

salopp abwertend
  1. unsachgemäß, fehlerhaft, schlecht ausgeführte Arbeit
 

In·s·ti·tu·ti·o·na·li·sie·rung

Die Fehler in der Umsetzung von agilen Methoden durch zusätzliche Bürokratie beheben. Wenn sich die Teams auf die Füße treten, werden Merge Meetings eingeführt, anstatt die Reibungsflächen im voraus zu minimieren.

 

Überall nur Bäume hier, Entscheidungsbäume

“Ein Baum ist ein Wunder – der Wald bewirkt Wunder.”
― Klaus Ender

Meinen letzten Text zur Künstlichen Intelligenz schrieb ich in den neunziger Jahren, als Werkstudent am Lehrstuhl für Künstliche Intelligenz. Jetzt also widme ich mich, dem aktuellen Hype geschuldet, wieder einmal diesem Thema. Es ist mehr als 25 Jahre vergangen und eine Menge hat sich getan, die Verfahren sind qualitativ und quantitativ besser geworden, die Rechner schneller und der Speicher größer.

Aber immer wieder zeigen sich die klassischen Probleme der KI, wenn man das Kleingedruckte zu den euphorischen Erfolgsmeldungen liest. Man weiß nicht so genau, was die neuronalen Netze in ihren versteckten Schichten eigentlich lernen, die Expertensysteme kränkeln an ihren ganz eigenen Dunning-Kruger-Effekten.

Wie  der Titel schon andeutet, möchte ich ein wenig über Bäume reden, uns Informatikern die liebste Pflanze. In diesem speziellen Fall geht es um den Entscheidungsbaum. Der Entscheidungsbaum wird genutzt um Objekte zu klassifizieren. Dabei ist es egal, ob es sich um Früchte, beschädigte Nähmaschinen oder Singles handelt. Im didaktischen Bereich klassifiziert man gerne Äpfel, Bananen und Orangen, im kommerziellen Umfeld eher beziehungssüchtige Akademiker. Beschäftigt sich jemand mit Nähmaschinen, dann heißt die Domäne womöglich IoT oder Maschine Learning.

Der Entscheidungsbaum an sich ist trivial, jeder Knoten steht für eine Entscheidung, um die Menge der Klassen für ein Objekt einzuschränken. Stellen wir uns einen ersten Knoten vor, der runde von nicht runden Früchten trennt und schon können wir Bananen von Äpfeln und Orangen unterscheiden. Gute Entscheidungsbäume reduzieren die Anzahl möglicher Klassen zügig, indem die aussagekräftigsten Attribute genutzt werden.

Ohne Künstliche Intelligenz wären wir hier vermutlich schon am Ende der Geschichte angelangt, denn ein Experte kann mit dem hier erworbenen Wissen seine eigenen Entscheidungsbäume erstellen.

Interessant wird die Sache aber erst, wenn wir maschinelles Lernen einsetzen, um die Bäume zu erstellen. Beim maschinellen Lernen wird ein Entscheidungsbaum basierend auf Beispielen erstellt. Der Algorithmus bekommt also von uns ein paar Äpfel, Orangen und Bananen mit ihren Attributen, wie Farbe, Größe, Form, Gewicht und Geschmack und wir bekommen dafür einen Entscheidungsbaum. Dieser Entscheidungsbaum ist in der Regel vom Algorithmus so erstellt, dass die Beispielobjekte mit einer minimalen Anzahl von Entscheidungen gefunden werden können. Welche Attribute dafür verwendet werden, hängt von den Beispielen ab. Haben wir zum Beispiel nur grüne Apfel, dann ist die Farbe eine exzellente Wahl um grüne Äpfel von orangen Apfelsinen und gelben Bananen zu unterscheiden.

Ein Entscheidungsbaum leidet wie die Expertensysteme am Cliff-and-Plateau-Effekt, dem Dunning-Kruger-Effekt für Maschinen. Halten sie unserem Entscheidungsbaum eine Möhre vor die Nase, dann erkennt er eine Banane. Wir können unseren Entscheidungbaum verbessern, in dem wir die Möhre und andere Früchte und Gemüse in unsere Beispielmenge aufnehmen. Das führt aber dazu, dass der Baum extrem groß und unübersichtlich wird und es lange dauert diesen Baum zu trainieren.

Eine Möglichkeit dieses Problem zu umgehen, ist  der Einsatz eines Random Forrest. Statt eines einzelnen gigantischen Baumes erstellt man eine Menge kleiner Bäume, die jeweils auf einer zufälligen Teilmenge der Beispiele erstellt werden. Diese Bäume können schnell trainiert werden und obwohl jeder einzelne nicht alle Objekte korrekt klassifizieren kann, kann dieser Wald durch Mehrheitsentscheidung sehr gute Klassifikationen liefern.

Wenn sie also mal wieder den Wald vor lauter Bäumen nicht sehen können, so trösten sie sich. Manchem Computer geht es genauso.

 

Immer Ärger mit Hierarchien

“Es dürfte Ihnen schwer fallen zu erklären, warum Sie eine Leiche vergraben, die Sie nicht getötet haben.”
aus dem  Film “Immer Ärger mit Harry”

In Scrum arbeiten selbstorganisierte, selbständige Teams an der Entwicklung der Software. Sie stellen am Ende eines jeden Sprints ein neues lauffähiges System für den Kunden bereit.

Auf diesem System kann der Kunden arbeiten und Erfahrungen sammeln. Dabei wird er feststellen, dass er vielleicht gewisse Features noch vermisst, andere als unnötig empfindet, entdeckt vielleicht Fehler oder im angenehmsten Fall, ist er von der neuen Version begeistert.

Neue Anforderungen des Kunden werden vom Product Owner in Form von Stories an das Team übergeben und es liegt in der Verantwortung des Product Owner, die neuen Anforderungen verständlich und nach absteigenden Business Value dem Team zur Verfügung zu stellen.

In der Verantwortung des Teams liegt es, so viele Features wie möglich im Sprint umzusetzen. Neben einer schnellen und korrekten Umsetzung muss das Team auch für eine nachhaltige Entwicklung sorgen. Dazu muss es auf die Qualität, die Wartbarkeit, die Erweiterbarkeit und die Testbarkeit der Software achten.

Was hat dieser kurze Blick auf Scrum denn nun mit Hierarchien zu tun und wieso bedeuten Hierarchien Ärger? Eine Hierarchie verursacht Probleme, wenn ihre Grundmotivation den Prozesszielen entgegensteht. Ob der Prozess nun Scrum, XP, Kanban, Wasserfall oder V-Modell ist, wenn die Triebfeder der Führungskräfte nicht aus dem jeweiligen Prozess heraus entsteht, führt dies zu massiven Störungen.

Der halbherzige Schritt in Richtung Scrum, der alte Strukturen in der Firma unberührt lässt, ist eine selbsterfüllende Prophezeiung des Scheiterns. Schon Parkinson formulierte in seinen Gesetzen zur Arbeit, dass sich Hierarchien erhalten wollen und sich nicht von selber optimieren. Daher werden Management Pyramiden die für Umsätze und Erfolgsmeldungen honoriert werden, sich immer in die Arbeit ihrer Untergebenen einmischen.

Neben den Führungskräften, die Scrum ablehnen und ganz bewusst den neuen agilen Ansatz torpedieren, gibt es aber auch viele, die an der Reibungsfläche zwischen Führung und Autonomie scheitern.

Grundsätzlich Fehler machen Führungskräfte, die versuchen den Prozess von außen zu optimieren. Ein beliebtes Werkzeug ist Meetings nur noch mit den wichtigen Mitgliedern durchzuführen und dadurch Zeit zu sparen. Dies ist natürlich eine Milchmädchenrechnung, da die interne Kommunikation im Team erhöht wird und sich Fehler durch diese Art von Stille Post einschleichen. Die Trennung zwischen wichtigen und unwichtigen Mitgliedern treibt einen Keil ins Team, der die Motivation einzelner Mitarbeiter und das Zusammenspiel im Team gefährdet. Oft kommen die gewichtigen Einwände von den Kollegen, die in den Augen der Führungskräfte nur kritisch, anstrengend und übervorsichtig sind und deshalb natürlich nicht zu Meetings eingeladen werden. Wenn dann eintritt, was diese Kollegen vorhergesagt haben, hört man nicht umsonst ein verächtliches “Das hätte ich euch sagen können” auf den Fluren.

Direkten Druck auf das Team auszuüben, ist ein weitaus schwierigeres Unterfangen, dass meist von den Teams und ihren Scrum Master abgeblockt  werden kann. Da das Team über seine Commitments den Zufluss der Arbeit steuert, kann hier nur an das Gewissen der einzelnen Kollegen appelliert werden.  Das funktioniert einige Male, aber dann muss die Führungskraft sich der Eitelkeit der Entwickler bedienen. Nichts schmeichelt einem Mitarbeiter mehr als Sonderaufgaben oder die Wahrnehmung der eigenen Expertise durch Führungskräfte. Kurzfristig ein Erfolgsmodell, dass aber schnell zum Bumerang wird, weil der Mitarbeiter zum Außenseiter im Team wird. Die Performance fällt und das Team und der Scrum Master müssen die Situation klären.

Die fatalsten Schäden werden aber verursacht, wenn die Führungskräfte der Idee verfallen, man könne Projektaufgaben beliebig über die Teams verteilen, oder schon Teilarbeiten von Stories irgendwo dazwischen drücken. Das dies überhaupt möglich ist, liegt an einer Firmenkultur, in der Scrum noch nicht angekommen ist. Jedes vernünftige Team sollte Stories für ein fremdes Projekt, die es ohne Absprachen mit dem verantwortlichen Team vorgestellt bekommt, ablehnen. Benötigt ein Projekt mehrere Teams, dann müssen diese Teams durch ein Scrum-konforme Mechanismen, wie etwa Scrum of Scrum oder Nexus zusammengehalten werden und nicht durch den Product Owner. Der Product Owner kann die technische Kopplung zwischen den Teams nicht gewährleisten, da seiner Rolle die entsprechenden Fähigkeiten und Befugnisse fehlen.

Wer Geld verschwenden will, der gönnt sich eine vielschichtige Hierarchie über seine Teams. Alle anderen sorgen für mehr Luft, damit sich Scrum entfalten kann.

 

 

 

 

 

 

Kobayashi-Maru

Der Kobayashi-Maru-Test ist ein fiktives Übungsszenario, dass in mehreren Filmen und Episoden von Star Trek erwähnt wird. In dem Test wird geprüft, wie Kadetten in einer ausweglosen Situation handeln. Obwohl niemand diesen Test bestehen kann, hat der Kadett James T. Kirk den Test erfolgreich absolviert, indem er das Übungsszenario manipulierte.

Der Begriff Kobayashi-Maru steht für ausweglose Projektsituationen oder toxische Teamkonstellationen, die nur noch durch einen Abbruch beendet werden können.

 

 

Dependency Injection mit ParameterResolver in JUnit 5

JUnit 5 bieten eine ganze Reihe neuer Möglichkeiten um Unit Tests kompakt und strukturiert zu schreiben. Unter anderen gibt es nun die Möglichkeiten die Test Methoden mit Parametern zu versehen. Genauer gesagt können Konstruktoren und alle Methoden, die mit @Test@RepeatedTest@ParameterizedTest@TestFactory@BeforeEach@AfterEach@BeforeAll oder @AfterAll annotiert sind, um Parameter ergänzt werden.

Die Parameter werden je nach Typ beim Aufruf der Methoden mit den entsprechenden Werten belegt.

@Test @DisplayName("display name") @Tag("tag")
void test1(TestInfo info) {
  assertEquals("display name", info.getDisplayName());
  assertTrue(info.getTags().contains("tag"));
}

Hier in diesem Beispiel wird der Parameter info mit einer TestInfo Instanz belegt. Die aktuellen Werte werden von einem in JUnit 5 bereitgestellten ParameterResolver befüllt. Entwickler können aber auch eigene ParameterResolver implementieren.

Ein einfaches Beispiel für die Verwendung eines ParameterResolvers ist das Bereitstellen eines JsonNodes aus einer Datei für die Tests. Um die Aufgabe etwas anspruchsvoller zu gestalten, soll es für jeden Test eine andere Datei sein. Selbstverständlich kann man dieses Problem auch schon mit älteren JUnit Versionen lösen.

private JsonNode load(String resource) {
  return new ObjectMapper().readTree(getClass().getResourceAsStream(resource));
}

Diese Methode kann man nun in jedem Test aufrufen. Sie existiert in der speziellen Testklasse oder verschwindet in einer von vielen Utility Klassen. Die Wiederverwendung dieser Methode in einem anderen Kontext ist nicht sehr wahrscheinlich.

class Test4 {
  @Test
  public void testWithJUnit4() {
    JsonNode node = load("junit4.json");
    assertEquals("JUnit 4", node.textValue()); 
  }
}

Mit JUnit 5 und eigenem ParameterResolver kann der Test kompakter geschrieben werden.

@ExtendWith(JsonNodeParameterResolver.class)
class Test5 {
  @Test 
  public void testWithJUnit5(@Resource("junit5.json") JsonNode node) {
    assertEquals("JUnit 5", node.textValue()); 
  }
}

Dazu erstellen wir einen eigenen ParameterResolver für Parameter vom Typ JsonNode und eine Annotation @Resource, die den Namen der Quelldatei bereitstellt. Der ParameterResolver sorgt auch dafür, dass die benötigten Objekte nur für diesen speziellen Test erzeugt werden. Im Gegensatz zur Setup Methode, die häufig Objekte erzeugt, die im jeweiligen Test nicht benötigt werden.

public class JsonNodeParameterResolver implements ParameterResolver {
  @Override
  public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
    return parameterContext.getParameter().getType().equals(JsonNode.class);
  }
  @Override
  public ObjectNode resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
    Resource annotation = findAnnotation(parameterContext.getParameter(), Resource.class);
    if (annotation == null) {
      throw new ParameterResolutionException("no @Resource annotation found");
    }
    String resource = annotation.value();
    try {
      return (ObjectNode) new ObjectMapper().readTree(extensionContext.getTestClass().get().getResourceAsStream(resource));
    } catch (IOException e) {
      throw new ParameterResolutionException("could not load Json from " + resource, e);
    }
  }
}

So bekommen wir nicht nur eine saubere Trennung zwischen Support- und Test-Code, auch die Wiederverwendbarkeit ist durch einen ParameterResolver verbessert.

 

Mehr zum Fluent Builder

Kaum erscheint der Beitrag  Fluent Builder statt komplexe Konstruktoren, da regt sich auch schon der Unmut bei den Lesern.

Denn der Verwechslungsgefahr bei den verschiedenen Parametern, kann auch durch eigene Typen für Vorname, Nachname und Anrede vorgebeugt werden.

public final class FirstName {
  private String name;
  public FirstName(String name) {
    this.name = Objects.requireNonNull(name);
  }
  public String value() {
    return name;
  }
}

Diese Klasse verwenden wir anstelle des String Typen und können so einer versehentlichen Parametervertauschung vorbeugen. Unser Beispiel mit Mary Poppins wird dann wie folgt geschrieben.

Person.female().with(new Title("Ms")).with(new FirstName("Mary")).with(new LastName("Poppins")).get();

Die Methodennamen reduzieren wir auf with, da der Parametertyp die Methode erklärt.

Momentan kennen wir für die Anrede nur die Werte Ms, Mr und Mrs, daher ersetzen wir den Typen Title durch ein Interface. Dieses Interface lassen wir von einer Enum StandardTitle implementieren. Damit greifen wir in unserem Code auf Konstanten zurück, berauben uns aber nicht der Möglichkeit eine andere Anrede zu verwenden.

public interface Title {
  String value();
}

public enum StandardTitle implements Title {
  MS, MR, MRS;

 @Override
 public String value() {
   return name();
 }
}

Unser Beispiel mit einem static import verkürzt sich auf

Person.female().with(MS).with(new FirstName("Mary")).with(new LastName("Poppins")).get();

Eine Alternative oder Ergänzung zum Fluent Builder ist der Einsatz eines Prototypen. Wir ergänzen die Klasse Person dazu um Methoden, die modifizierte Kopien der prototypischen Instanz liefern. Bei diesem Ansatz können alle erzeugten Kopien immutable sein. Die Werte der Kopien können wir über den Konstruktor setzen.  Wir benötigen somit keine Setter-Methoden mit denen die Instanzen versehentlich verändert werden können.

public Person with(FirstName firstName) {
  Person result = new Person(this).
  result.firstName = Objects.requireNonNull(firstName);
  return result;
}

Die erste Zeile liefert eine Kopie der aktuellen Instanz, deren firstName Attribute in der zweiten Zeile ersetzt wird.

Es gibt eine Menge Möglichkeiten, wie man die Zuständigkeiten zwischen Builder und Prototype aufteilt. Da wir in unserem Beispiel davon ausgehen, dass nur das Geschlecht in den Instanzen gesetzt sein muss, können wir unseren Builder auf die beiden Methoden male() und female() reduzieren, die ein Objekt vom Typ Person zurückliefern. Alle weiteren Modifikationen erfolgen dann über den erzeugten Prototypen und es entfällt die get() Methode des Builders.

Person.female().with(MS).with(new FirstName("Mary")).with(new LastName("Poppins"))

Der Einsatz spezifischer Klassen für die Parameter, vereinfacht nicht nur die Verwendung des Builders, sondern verbessert auch die Verständlichkeit der gesamten Codebasis. Die spezifischen Fachlichkeiten von Vorname, Nachname und Anrede werden in den entsprechenden Klassen formuliert, was im vorher verwendeten Typ String nicht möglich war.

 

Fluent Builder statt komplexe Konstruktoren

“Code is like humor. When you have to explain it, it’s bad.”
– Cory House

Immer wieder trifft man auf Klassen die diverse Parametern für ihre Konstruktoren gebrauchen. Abgesehen davon, dass zu viele Parameter ein Code Smell sind, kann es je nach Typ der Parameter auch schon einmal zu einer Verwechslung kommen.

public Profile(String firstName, String lastName, String title, LocalDate birth, Gender gender) {
}

Der obige Konstruktor besitzt drei String Parameter und ein unvorsichtiger Entwickler hat diverse Möglichkeiten ihn fehlerhaft aufzurufen.

new Profile("Mr", "Chico", "Marx", LocalDate.of(1887, Month.MARCH, 22), Gender.MALE);
new Profile("Harpo", "Mr", "Marx", LocalDate.of(1888, Month.NOVEMBER, 23), Gender.MALE);
new Profile("Groucho", "Marx", "Mr", LocalDate.of(1890, Month.OCTOBER, 2), Gender.MALE);
new Profile("Marx", "Gummo", "Mr", LocalDate.of(1892, Month.OCTOBER, 23), Gender.MALE);
new Profile("Marx", "Mr", "Zeppo", LocalDate.of(1901 Month.FEBRUARY, 25), Gender.MALE);

Von den fünf dargestellten Konstruktoren, ist nur der für Groucho Marx korrekt. Eine andere Art der Konstruktion ist die Verwendung eines fluent Interfaces. Sehr vereinfacht handelt es sich bei einem fluent Interface um eine sprechende Schnittstellen mit einer Grammatik. In unserem Beispiel etwa

Profile.firstName("Chico").lastName("Marx").title("Mr").birth(LocalDate.of(1887, Month.MARCH, 22)).male();

In diesem Fall wäre firstname() eine statische Methode die ein Objekt mit dem Vornamen erzeugt und die weiteren Methoden würden die anderen Werte hinzufügen.  Jede dieser Methoden würde das aktuelle Objekt vom Typ Profile zurückliefern und somit würde nach und nach das Objekt vollständig befüllt.

public static Profile firstName(String firstName) {
  return new Profile(firstName);
}
public Profile lastName(lastName) {
  this.lastName = Objects.requireNonNull(lastName);
  return this;
}

Für einfache Anwendungsfälle ausreichend, wenn jedoch alle Attribute gesetzt sein müssen, oder eine spezielle Untermenge aller Attribute, dann ist dieser Ansatz sehr fehleranfällig. Strikter in der Verwendung und weniger fehleranfällig wird der Ansatz, wenn die Methoden intermediäre Objekte zurückliefern, die dem Entwickler die weiteren Schritte durch eine Grammatik vorgeben.

Person.female().firstName("Winifred").lastName("Banks").get();
Person.male().firstName("George").lastName("Banks").get();

Hier liefern die Methoden female() und male() intermediäre Objekte vom Typ FemaleBuilder, bzw. MaleBuilder, die dafür sorgen, dass am Ende entweder Frauen mit der Anrede “Mrs“, bzw. Männer mit der Anrede “Mr” erzeugt werden. Die anderen Methoden arbeiten auf diesen intermediären Objekten und erst der Aufruf von get() erzeugt dann ein Person Objekt.

class FemaleBuilder {
  private Profile profile = new Profile(Gender.FEMALE);
  FemaleBuilder firstName(String name) {
    profile.name = Objects.requireNonNull(name);
    return this;
  }
  FemaleBuilder title(String title) {
    profile.title= Objects.requireNonNull(title);
    return this;
  }
  return Profile get() {
    return profile;
  }
}
public static FemaleBuilder female() {
  return new FemaleBuilder();
}

Da es natürlich auch unverheiratete Frauen gibt, besitzt der FemalBuilder noch eine explizite title() Methode. Da in unserem Beispiel Männer keine andere Anrede haben können, gibt es diese Methode nicht in MaleBuilder.

Person.female().title("Ms").firstName("Mary").lastName("Poppins").get();

Insgesamt benötigt eine fluent Implementierung mehr Aufwand als ein einfacher Konstruktor, aber das bessere Verständnis und der weitaus geringere Dokumentationsaufwand ist diesen Aufwand wert.

 

 

 

 

 

 

 

The Sixth Sense of Company Culture

“I see people, they don’t know they are dead” – Cole Sear

You may know Cole Sear, the young boy from the movie “The Sixth Sense”. He saw and spoke with dead people, most of them angry, sad or lethargic. They all didn’t realized, that they are dead. They ignored all signs and tried to life on.

You can see the same in dead companies, too. A company is dead, if the employees cannot trust the management. The employees act like the dead people from the film, angry and lethargic. The business goes on, but there is bad mood on the floors, productivity and innovations down. They have a company culture, which is written down in the coporate identity brochure, but nobody tries to live it.

 

 

SNAFU

SNAFU (Situation Normal, All Fucked Up) ist ein Akronym aus der amerikanischen Soldatensprache. Das SNAFU-Prinzip von den Roman Autoren Robert Anton Wilson und Robert Shea besagt, dass innerhalb von Hierarchien immer wichtige Informationen zurückgehalten werden.

Vorgesetzte geben nicht alle Informationen an ihre Untergebenen weiter, um einen Informationsvorsprung zu behalten oder ihre Inkompetenz zu kaschieren. Untergebene verheimlichen ihrerseits Probleme und Inkompetenzen oder weisen den Vorgesetzten nicht auf seine Fehler hin.  Das SNAFU-Prinzip treibt eine Organisation immer in Richtung Chaos und je höher eine Hierarchie ist, desto weiter entfernt voneinander, sind die Wahrheiten von Spitze und Fundament.