Große Softwareprojekte und Lernen

23.06.2010 Permalink

Die halbe Wahrheit ist: ein Softwareprojekt erzeugt ein Softwareprodukt. Die andere Hälfte ist: notwendigerweise entsteht dazu immer eine Projektorganisation, also eine Struktur aus Rollen und Teams und den Prozessen, die von Mitarbeitern ausgeführt werden, damit am Ende ein auslieferfähiges Release entsteht. Große Softwareprojekte sind komplex, und folgerichtig ist auch die Organisation komplex. Außerdem sind Softwareprojekte per Definition einzigartig, jedes Projekt "erfindet" also seine individuelle Projektorganisation, obwohl es Ähnlichkeiten gibt.

Einzigartig und Komplex. Das klingt nicht danach, als würden die beteiligten Menschen sofort alles richtig machen können. Und daher ist es keine Überraschung, dass kaum ein Drittel aller Softwareprojekte, die der CHAOS Report 2009 der Standish Group untersucht, die ursprünglichen Ziele trifft. Alle anderen Projekte verpassen die Ziele oder erbringen gar kein nutzbares Ergebnis. Die Ursachen von Schwierigkeiten entstammen der Gemengelage aus Zeit- und Kostendruck, unklarer oder unrealistischer Zielsetzung, sich ändernden Rahmenbedingungen, mangelndem Rückhalt in der durchführenden Organisation oder auch Qualifikationsdefiziten der beteiligten Projektmitarbeiter.

Fakt ist: diese Gemengelage existiert schon lange, und wir beklagen sie, aber sie wird auch in der Zukunft immer bleiben. Wir -- als Softwareingenieure -- können mit dem Finger auf andere zeigen und behaupten: unter diesen Bedingungen kann man keinen Erfolg haben. Aber ändern wird sich dadurch nichts.

Nehmen wir es daher hin: große Softwareprojekte sind riskant, weil sie in widriger Umgebung starten, schwierig zu organisieren sind, und praktisch niemand der Beteiligten mit diesem individuellen Vorhaben Erfahrung hat, denn ein Projekt ist eine einmalige Angelegenheit. Aber wir sind dieser Situation nicht ausgeliefert, ganz und gar nicht. Sie entspricht grundsätzlich all jenen Situationen, in denen wir lernen müssen. Und da Menschen zumindest in ihren jungen Jahren ständig lernen, besitzen wir alle längst die Schlüsselqualifikation, um große Projekte in den Griff zu kriegen.

Wie funktioniert praktisches Lernen? In meinen eigenen Worten etwa so:

  1. Begreifen, was wir wollen
  2. Abgucken, wie andere dort hinkommen
  3. Selbst handeln
  4. Vergleichen unseres Ergebnis mit unserem Ziel
  5. Verändern unserer Handlungsidee, damit's beim nächsten Mal besser klappt
  6. Wiederholen
Das Entscheidende für eine Verbesserung ist die Rückkopplung durch 4. und 5. Wenn wir uns iterative und insbesondere agile Entwicklungsprozesse ansehen, dann erkennen wir sofort, das genau diese Rückkopplung zentraler Bestandteil ist. Das Lernen ist hier schon eingebaut. Wir sollten uns Rückkopplungen aber an viel mehr Stellen zunutze machen als nur an einem Iterationsende, denn wir wissen: je schneller wir Feedback zu einer Handlung bekommen, desto genauer können wir unser Handeln reflektieren, und desto leichter können wir Qualitätsmängel beseitigen. Der richtige Einsatz von Rückkopplungen sollte uns in unserem Projekt ultimativ sogar erlauben, Fehler zu vermeiden anstatt sie hinterher finden und beseitigen zu müssen.

Bis hierher habe ich nur eine abstrakte Idee genannt, was auch große Projekte beherrschbar macht. Ich möchte nun mit ein paar Beispielen zeigen, wie konkrete Rückkopplungen aussehen und wirken:

Schätzungen und Ist-Aufwandserfassung
Aufwandsschätzungen, die auf Basis einer Grobbeschreibung eines künftigen Softwaresystems entstehen, besitzen eine Genauigkeit von [0,25;4], mit anderen Worten: jede von einem Entscheider akzeptierte Aufwandszahl ist mit extremer Unsicherheit behaftet. Man könnte auch sagen: wir wissen nicht, wie teuer es wird. Zum Teil rührt diese Unsicherheit aus all den in dieser frühen Phase noch nicht getroffenen Entscheidungen her, zum Teil aber auch, weil das Team seine Produktivität, d.h. wieviel nutzbare Funktionalität pro PT herausspringt, nicht kennt. Unterschiedliche Teams sind unterschiedlich produktiv, sehr schlechte Teams verbrauchen ca. fünfmal mehr Aufwand als sehr gute. Um eine vernünftige Idee zu bekommen, ob ein Budget reicht, muss das Team früh eine Teilfunktionalität schätzen, erstellen, den tatsächlichen Aufwand festhalten und auf dieser Datenbasis die Budgethöhe plausibilisieren. Diese Rückkopplung kann sehr viel Geld sparen, wenn man z.B. durch sie feststellt, dass der Business Case nicht mehr aufgeht. Läuft das Projekt weiter, so lernt das Team mit dieser Rückkopplung, besser zu schätzen. Das Projekt wird vorhersagbar.

Benutzertests
Wenige Softwareentwickler haben ein Talent, Benutzerschnittstellen so zu bauen, dass Nicht-Technik-Verliebte sie als intuitiv bedienbar empfinden. Hat man eine große Zahl von Benutzern als Zielgruppe für ein Softwaresystem, so ist Benutzertauglichkeit kritisch: lehnen die Benutzer die Software ab, droht dem System das frühe Aus. Das Team muss also lernen, wie eine gute UI aussieht, und dazu braucht es als Rückkopplung Benutzertests. Und zwar sehr früh, denn UI-Entwicklung ist erstaunlich aufwändig und mit UI-Prototypen findet man nicht selten wesentliche Anforderungen an das System, die sonst übersehen würden. Wartet man zwei bis drei Iterationen, bis genügend Funktionalität durch Benutzer erreichbar ist, hat man vielleicht wertvolle Zeit verschenkt.

Continuous Integration
Große Software besteht aus vielen tausend Artefakten, von denen einige wenige eng mit der Arbeitsumgebung des Entwicklers verknüpft sind. Ein Entwickler kann sicherstellen, dass die Funktionalität, die er überblickt, auch nach seiner Codeänderung in seiner Umgebung in Ordnung ist, aber er kann kaum umfassend garantieren, dass nach seinem Checkin sämtliche Funktionalität auch in einer vollkommen unabhängigen Umgebung vorhanden ist. Continuous Integration hilft: hat sich etwas im Versionskontrollsystem geändert, dann wird das System wenige Minuten später vollständig gebaut und automatisiert getestet, und zwar in einer eigenständigen Umgebung. Fällt hierbei ein Fehler auf, wird der Entwickler automatisch informiert. Diese Rückkopplung erfolgt in weniger als einer Stunde, und der Entwickler kann ohne Rüstzeit die Fehlerbeseitigung vornehmen.

Frühe Lasttests
Ob ein System der Last im Wirkbetrieb gewachsen ist, kann man nur spät auf einer wirkbetriebsnahen Testumgebung feststellen. Es gibt allerdings eine ganze Reihe von Architekturschwächen und Programmierfehlern, die sich erst unter Last bemerkbar machen, aber sehr viel früher aufzudecken sind. Da reicht heute i.d.R. der PC des Entwicklers und ein OpenSource Lastgenerator (z.B. JMeter), damit das Projekt früh lernt, wie die Architektur verbessert werden muss. Diese Rückkopplung verhindert, dass späte weitreichende Umbaumaßnahmen erforderlich werden, um das gewünschte Skalierungsverhalten zu erzielen.

Ich könnte diese Beispielbeschreibungen fortsetzen, doch ich beschränke mich auf ein paar weitere Schlagworte zu Maßnahmen, die uns beim Lernen helfen: Code Reviews, Unittests, Softwaremetriken, Lessons Learned Meetings, Feedbackgespräche unter vier Augen, kurze Releasezyklen, System- und Integrationstests, Security Audits, Architekturdurchstich, Laufzeitmonitoring, ja selbst so etwas Selbstverständliches wie ein Compiler erzeugt laufend Rückkopplungen Richtung Entwickler.

Nur wenige Maßnahmen sind an ein Iterationsende gebunden, manches kann als fester Prozessschritt eingeführt, anderes als Einzelaufgabe behandelt oder gar durch geeignete Werkzeuge automatisiert werden. Als Projektleiter habe ich eine Ahnung, wo die Fallstricke liegen können, doch erst die konkreten Rückkopplungen retten mein Team vor dem Unfall.

Große Softwareprojekte sind schwierig, aber nicht unbeherrschbar. In jedem einzelnen muss das Team erst lernen, es zu beherrschen. Das Team muss sich also die Frage stellen, wie es effizient lernen kann. Und das wichtigste Mittel dazu sind gezielt gesetzte Rückkopplungen.