Über unsMediaKontaktImpressum
Urs Enzler 10. März 2015

Effektive Teams sind kein Zufall

Ein funktionierendes Softwareteam ist mehr als eine zusammengewürfelte Gruppe von Entwicklern. In effektiven Teams bilden sich Muster, welche helfen, das Projektziel früher und besser zu erreichen. In diesem Artikel erkläre ich die Muster, welche ich in Teams immer wieder beobachte, und zeige, wie Sie diese in Ihr Team einbringen können.

 

Die richtigen Entscheidungen treffen

Effektive Teams zeichnen sich dadurch aus, dass sie die richtigen Entscheidungen treffen, um nur die Arbeiten auszuführen, welche das Projekt oder Produkt voranbringen. Die folgenden sechs Muster beschreiben, wie die Effektivität eines Teams verbessert werden kann.

Muster 1: Free Jazz

Im Free Jazz ist die Improvisation ein wichtiger Teil. Doch die Musiker spielen nicht einfach das, wozu sie gerade Lust haben. Wenn der Saxophonist das Solo des Trompeters ablöst, dann übernimmt er meist einen Teil der Melodie und beginnt dann, diese zu verändern.
Als Team sollten wir auf dieselbe Art Software entwickeln – wir sollten auf bestehenden Lösungen aufbauen.

Nacharbeiten

Leider tendieren wir zu viel Nacharbeit:

  • Ein Entwickler wirft den Code des Kollegen weg, weil er dessen Code-Stil nicht mag.
  • Die Teammitglieder haben unterschiedliche Ziele und passen den Code ihren Zielen an, statt auf ein gemeinsames Teamziel hinzuarbeiten.
  • Das Team leidet am „not-invented-here“-Syndrom und arbeitet an Dingen, welche bereits durch kommerzielle oder Open-Source-Bibliotheken gelöst sind.

Deshalb brauchen wir mehr Free Jazz in unseren Teams.

Gemeinsames Ziel und Vorankommen auf Basis existierender Lösungen

Wir benötigen ein gemeinsames Ziel. Jeder im Team muss das Projekt- oder Produktziel kennen und danach handeln. Wir benötigen ein gemeinsames Verständnis, wie die Software gebaut werden soll. Dies reicht vom Code-Stil über Design-Stil bis hin zu Architekturentscheiden.

Das Team muss einen Weg finden, wie es Software bauen kann, indem es sich immer vorwärts bewegt. Viele Teams sind gefangen in einem Zyklus „ein Schritt nach vorne, einen Schritt zurück“. Das Team muss lernen, Funktionalität zur Software hinzuzufügen, indem es auf existierenden Lösungen aufbaut. Dies können interne, selbst entwickelte Lösungen sein, oder externe Lösungen von Drittherstellern oder Open-Source-Bibliotheken.

Free Jazz lernen

Es braucht Zeit, wenn Sie eine Free-Jazz-Mentalität in Ihrem Team wollen. Pair Programming [1] und Coding Dojos [2] sind zwei gute Hilfsmittel, um diese Art der Zusammenarbeit zu erlernen:

Das Team sollte Pair Programming praktizieren. Die zwei pair-programmierenden Entwickler nehmen verschiedene Rollen ein. Der Driver tippt und kümmert sich um alle Details (Strichpunkte, Variablennamen usw.). Der Navigator ist verantwortlich, dass das Pair auf dem richtigen Weg bleibt und das richtige Problem löst. Während des Pair Programming lernen die zwei Entwickler, eine Lösung gemeinsam zu entwickeln, auf den Ideen des anderen aufzubauen und diese zu einer Lösung zu kombinieren.

In Coding Dojos sitzt das ganze Team in einem Sitzungszimmer. Zwei Entwickler praktizieren Pair-Programming auf einem Laptop, der mit einem Projektor verbunden ist. Das Team löst zusammen eine Übungsaufgabe. Alle zehn Minuten wird rotiert. Der Driver verlässt den Laptop, der Navigator übernimmt die Position des Drivers und ein Entwickler aus dem Publikum wird zum Navigator. Wenn alle Tests grün sind (ein Coding Dojo wird selbstverständlich mittels Test Driven Development [3] gelöst) kann das Publikum Kritik äußern. Solange ein Test rot ist, muss das Publikum stumm bleiben. Ein Coding Dojo ist eine sehr gute Übung für ein Team, um zu lernen, gemeinsam eine Lösung zu erarbeiten, bei der auf bestehendem Code aufgebaut wird und immer vorwärts gegangen wird. Die einzige Ausnahme ist, wenn sich das ganze Team darauf einigt, dass es sich in eine Sackgasse manövriert hat.

Muster 2: Den Tag planen

Um die richtigen Entscheidungen zu treffen und Doppelspurigkeiten zu verhindern, müssen alle Teammitglieder jederzeit Bescheid wissen, wer was gerade macht. Denn schlechte Koordination ist ein Hauptgrund für ineffektive Teams. Dinge werden mehrfach gemacht: „Oh, du hast das schon gemacht? Tja, ich auch!“. Unnötige Arbeiten werden ausgeführt, weil wir von falschen Annahmen ausgehen. Wir verpassen Chancen, früher live zu gehen, eine Funktionalität einfacher und günstiger zu implementieren oder eine gute Idee umzusetzen. Oder wir bauen sogar das Falsche aufgrund eines Missverständnisses.

Tägliches Teammeeting

In Scrum haben wir eine großartige Möglichkeit, uns jeden Tag im Team zu koordinieren: das Daily Scrum (auch als Daily Stand-up in anderen Prozessen bekannt). Wir beantworten die folgenden drei Fragen:

  1. Was ist seit dem letzten Daily Scrum passiert?
  2. Welche Probleme sind uns aktuell im Weg?
  3. Was mache ich bis zum nächsten Daily Scrum (und mit wem)?

Aber warum beantworten wir diese Fragen? Wir wollen, dass alle Teammitglieder voll informiert sind, wo wir gerade stehen:

  1. Was ist bereits getan und muss nicht mehr angefasst werden (keine Nacharbeit)?
  2. Was muss sich ändern, damit wir effizient arbeiten können?
  3. Alle kennen den Plan, wie wir bis zum nächsten Daily Scrum arbeiten wollen.

Daily Scrum behandelt Vergangenheit UND Zukunft!

Dies sind alles Aussagen über die Zukunft! Die meisten Daily Scrum, die ich gesehen habe, behandeln nur Dinge in der Vergangenheit. Das ist komplett falsch. Das Daily Scrum ist für die Zukunft da: wie wollen wir zusammen arbeiten, um möglichst viel an diesem Tag zu erreichen. Wir sprechen nur über die Vergangenheit, um zu wissen wo wir gerade stehen.

Daily Scrum auf Zukunft trimmen

Ihr Team sollte sich während des Daily Scrums auf die Zukunft fokussieren. Das fällt dem Team leichter, wenn das Daily Scrum möglichst früh morgens ist. Denn dann können wir wirklich den Rest des Tages zusammen planen. Wenn ein Teammitglied früh morgens ins Büro kommt, dann weiß es nicht, was die anderen am gestrigen späten Abend noch alles Fantastisches gemacht haben. Darum muss das Team diskutieren, zu welcher Zeit normalerweise alle Teammitglieder im Büro sind und das Daily Scrum dann abhalten. Dies ist ein guter, motivierender Start in den Tag.

Muster 3: Mit minimaler Lösung starten

Wenn wir ein Ziel vor Augen haben, dann tendieren wir dazu, uns zu überlegen, wie wir in einem großen Schritt dort hinkommen. Jedoch bewegt sich das Ziel in der Softwareentwicklung meistens, wenn wir uns ihm nähern. Entweder weil unser Kunde unseren Fortschritt sieht und eine bessere Idee hat oder weil wir das Problem besser verstehen und eine geeignetere Lösung finden. Weil wir aber in einem großen Schritt zum Ziel kommen wollten, haben wir bereits viel investiert:

  • Wir haben die ganze Lösung zu Beginn geplant.
  • Wir haben wahrscheinlich Code implementiert, der gar nicht mehr nötig ist und wieder entfernt werden muss. Das Resultat sind zusätzlichen Kosten.

Kleine Schritte

Effektive Teams gehen ein Problem in kleinen Schritten an. Sie wollen nicht in einem großen Schritt zum Ziel kommen, sondern sie gehen einen kleinen Schritt in Richtung des Ziels – ein minimales Inkrement. Dieses Vorgehen resultiert einerseits in viel weniger initialem Design und potentieller Verschwendung. Andererseits kann das Team schneller Feedback von den Stakeholdern einholen. Wenn sich nun das Ziel bewegt, dann richtet sich das Team neu aus und macht den nächsten kleinen Schritt in Richtung des neuen Ziels.

In kleinen Schritten arbeiten führt zu weniger Verschwendung, weil Feedback früher eingeholt und damit die Richtung überprüft werden kann, bevor unnötige Arbeiten ausgeführt werden.

Diese kleinen Schritte werden inkrementell und iterativ ausgeführt. Jedes Inkrement fügt eine kleine neue Funktionalität hinzu. Jede Iteration macht die bestehenden Features reichhaltiger. Das erste Inkrement eines Features ist oft ein Spike.

Spikes

Ein Spike ist eine minimale End-to-end-Lösung, welche fast keine Funktionalität liefert, aber durch das ganze System hindurchgeht (z. B. vom UI bis zur Datenbank). Um solch einen minimalen Spike zu ermöglichen, treffen wir viele Annahmen und machen Vereinfachungen: es gibt keine Fehlerfälle, der Benutzer macht keine Fehleingaben, der Benutzer versteht kryptische Datenoutputs und so weiter.

Dann iterieren wir über die Lösung und entfernen Annahmen und Vereinfachungen, wie z. B. das Fremdsystem ist doch nicht immer verfügbar, auf der Festplatte hat es keinen Platz mehr, der Benutzer macht doch Fehler. Wenn schließlich keine Annahmen und Vereinfachungen mehr vorhanden sind, dann erweitern wir die Funktionalität: zum Beispiel das Filtern oder Sortieren von Suchergebnissen.

Ein neues Feature mit einem Spike zu starten hilft uns, schnell Wissen aufzubauen. Wissen über das Feature an sich, über die eingesetzte Technologie und über unsere Lösung. Mit diesem frühen Wissen können wir die richtigen Entscheidungen treffen, wie wir mit der Umsetzung weiter gehen wollen. Wenn schließlich genügend Zeit ist, können wir die Funktionalität noch ausbauen. Wenn nicht, steht dem Release nichts im Weg, weil die Software stets lauffähig ist.

Mit kleinen Schritten schnell ans Ziel

Der Start mit einer minimalen Lösung und kleine Schritte helfen dem Team, Entscheidungen richtig und auf Basis ausreichender Information zu treffen. Detailentscheidungen werden erst getroffen, wenn die Big-Picture-Probleme bereits gelöst sind und das Team viel mehr über das Problem und die Lösung weiß.

Muster 4: Passt in meinen Kopf

Damit wir gute Entscheidungen treffen können, müssen wir die Problemstellung überblicken können. Ansonsten müssen wir ins Blaue hinein entscheiden.

Nicht annähernd verständlich

Heutzutage wird Software oft durch schnelles Hinzufügen von Feature um Feature gebaut, ohne über Verständlichkeit und Wartbarkeit nachzudenken.

Als Resultat finden neue Funktionalitäten den Weg in die Software immer langsamer, weil die Verständlichkeit sinkt. Einer der Hauptgründe dafür ist, dass ein Entwickler viel zu viel wissen muss, um nur schon eine einzelne Zeile Code zu ändern, ohne etwas anderes im System kaputt zu machen.

Alles ist miteinander verknüpft. Der Entwickler muss das ganze System im Kopf haben, um Auswirkungen von Änderungen abschätzen zu können. Und dies wird unmöglich nach kurzer Zeit im Projekt.

Bausteine

Der einzige Weg, unser Hirn nicht zu überfordern ist, das System in Einzelteile aufzuteilen, welche größtenteils unabhängig voneinander sind. Und dann das Gesamtsystem aus diesen Bausteinen aufzubauen. Wenn ich innerhalb eines Bausteins arbeite, dann kann ich die Umgebung ignorieren, solange ich die Schnittstelle und das von außen sichtbare Verhalten nicht ändere. Und beim Zusammenfügen von Bausteinen muss ich nur die Schnittstellen kennen und kann die Details der Implementation außer Acht lassen. So bin ich nicht mehr überfordert und kann meine Arbeit überblicken, schneller und mit weniger Fehlern arbeiten.

Muster 5: Immer auslieferbar

Ich sehe oft Teams, die zwar das Richtige tun wollen, aber nicht können. Ihre Software lässt sie nicht.

Der Code verhindert die richtige Entscheidung

Es gibt zu viele lose Enden im Quellcode. Alles wurde angefangen, aber nichts abgeschlossen. Es gibt schlicht zu viele offene Aufgaben. Oder es gibt sehr viel technische Schuld [4]. Bevor das Team das machen kann, was es braucht, muss zuerst die Schuld zurückgezahlt werden. Aber meist existiert die Zeit dafür nicht. Ein weiterer Grund ist der Mangel an Automation. Alles muss von Hand ausgeführt werden. Das ist fehleranfällig und langsam. Und alles dauert zu lange, um es richtig zu machen.

Als Folge wird hier und dort noch eine Abkürzung mehr genommen – aber dies macht alles nur noch schlimmer, wenn die nächste Entscheidung ansteht.

Änderungskosten und Reaktionsgeschwindigkeit

Die Kosten für eine Änderung sollten während eines Projektes konstant bleiben. Eine Änderung kostet heute genau so viel wie in einem Jahr. Die Reaktionszeit auf eine Änderung sollte während des ganzen Projekts hoch sein. Aber wenn sich technische Schulden einschleichen, dann steigen die Änderungskosten. Zu Beginn kann das Team durch Abkürzungen sogar Kosten sparen. Und die Kosten steigen nur langsam und damit unbemerkt. Irgendwann kommt dann der Zeitpunkt, an dem das Team bemerkt, dass es nicht mehr gleich schnell auf Änderungen reagieren kann. Das Problem ist dann, dass zu diesem Zeitpunkt schon viel technische Schuld angehäuft wurde. Es gibt keinen schnellen Ausweg aus dieser Situation. Der einzige professionelle Ansatz ist, technische Schuld von Anfang an begrenzt zu halten.

Clean Code

Es gibt nur einen Weg zu verhindern, dass sich technische Schuld anhäuft – das konsequente Schreiben von Clean Code [5]. Code, der so simpel und verständlich wie möglich ist. Code, der so einfach zu verstehen ist, dass sich keine Fehler darin verstecken können. Änderungen können gemacht werden, ohne dass Seiteneffekte auftreten.

Alles kontinuierlich

Clean Code alleine ist noch nicht genug. Um schnell eine Rückmeldung zu gemachten Änderungen zu erhalten, brauchen wir Continuous Integration [6] und Continuous Deployment [7] für jedes Commit ins Quellcode Repository. Alle Stakeholder (inklusive Entwickler, Tester, Usability-Experten) können jederzeit den aktuellen Stand der Entwicklung beurteilen. Dies führt zu einer immer lauffähigen Software, die jederzeit ausgeliefert werden könnte.

Entscheiden und sofort handeln

Dies gibt uns die Flexibilität, jederzeit Entscheidungen zu treffen. Sogar Entscheidungen, welche die Richtung des Projekts komplett ändern, können durchgeführt werden. Da es keine offenen Enden gibt, kann sofort in die neue Richtung gestartet werden. Es gibt keine lange To-do-Liste, die zuerst abgearbeitet werden muss.

Muster 6: Gute Ideen brauchen Zeit

Viele Scrum-Teams arbeiten Sprint für Sprint, ohne über die Sprintgrenze hinaus zu blicken. Diese Teams packen die Probleme zwar just-in-time an und definieren Anforderungen so spät wie möglich, um Verschwendung zu vermeiden. Dennoch haben sie ein großes Problem: gute Ideen brauchen Zeit, um zu entstehen. Eine gute Lösung für ein schwieriges Problem zu finden, dauert normalerweise länger als ein einzelner Sprint.

Product Backlog Refinement

In jedem Sprint führen wir einen Product Backlog Refinement Workshop durch. In diesem Workshop schauen wir uns an, was wir möglicherweise in den nächsten zwei bis drei Monaten entwickeln werden.

Wenn etwas am Horizont auftaucht, das großen Einfluß auf das jetzige System haben wird, dann diskutieren wir dies auf abstrakter Stufe: welche Systemteile sind betroffen, wie müssten diese geändert werden und wie würden sie untereinander kommunizieren. Wir sprechen auch über verschiedene Alternativen. Eventuell fügen wir einen Spike ins Product Backlog dafür ein, um sicherzustellen, dass wir zum Problem und möglichen Lösungen frühzeitig Wissen aufbauen. Wir diskutieren diese Dinge in jedem Product Backlog Refinement Workshop, bis sie umgesetzt sind. Jedes Mal ist die Diskussion etwas konkreter, mit weniger Varianten, damit wir dann bereit sind, wenn es an die Umsetzung geht.

Alle Änderungen mit mittlerem Einfluss diskutieren wir wesentlich später zum ersten Mal: etwa zwei Sprints vor dem geschätzten Umsetzungszeitpunkt.
Schließlich besprechen wir alles mit kleinem Einfluss im Product Backlog Refinement Workshop direkt vor dem Sprint, in welchem die Umsetzung folgt. Je näher die erwartete Umsetzung kommt, desto konkreter wird die Diskussion.

Gute Ideen – weniger Kosten

Durch das frühzeitige Diskutieren der Dinge mit großem Einfluss geben wir uns Zeit, um gute Ideen zu entwickeln. Die wiederholenden Diskussionen geben uns die Möglichkeit, eine Aufgabe aus verschiedenen Blickwinkeln zu betrachten – mit unterschiedlichen Lösungsideen. So haben wir wiederholt bessere Lösungen gefunden, Lösungen mit besserer Usability für den Benutzer und weniger Implementationskosten für uns. Meine Erfahrung sagt mir, dass wir etwa ein bis zwei Monate brauchen, um eine wirklich gute Idee zu entwickeln – meist unter der Dusche oder auf dem Sonntagsspaziergang.

Fazit

Effektive Teams sorgen stets dafür, dass genügend Information und Zeit vorhanden sind, um gute Entscheidungen treffen zu können. Die Entscheidungen bringen das Projekt oder Produkt dem Ziel immer etwas näher, betreffen aber immer nur einen überschaubaren Zeitrahmen in der Zukunft. Nur so können die Entscheide der sich ständig ändernden Umgebung gerecht werden und effektiv sein.

Quellen

[1] Wikipedia: Pair Programming   
[2] CCD-school: Coding Dojo            
[3] Wikipedia: Test-driven development
[4] Wikipedia: Technische Schuld
[5] Planetgeek: Clean Code
[6] Wikipedia: Continuous Integration
[7] Agile Alliance: Continuous Deployment

Autor

Urs Enzler

Urs Enzler arbeitet als Fachleiter Agile bei bbv Software Services AG. Er unterstützt Firmen bei der Einführung der agilen Denkweise und der Umsetzung von Projekten von der Idee bis nach der Auslieferung.
>> Weiterlesen
botMessage_toctoc_comments_9210