Mir gefällt die Metapher von der “wachsenden Software”. Ein schöner bildlicher Gegenentwurf zur Hausbaumetapher. Aber bisher hat mir dabei immer etwas gefehlt. Wie funktioniert denn das mit dem Wachsen genau? Software wächst ja nicht von allein, sondern durch unseren Eingriff von außen.
Das Buch “Growing Object-Oriented Software Guided by Tests” (GOOS) hat versucht, diesen Wachstumsprozess, nein, eher die Wachstumstätigkeit zu konkretisieren. Tests sollen das Wachstum antreiben und ausrichten.
Das ist sicher richtig, insofern Tests für Anforderungen stehen. Alles beim Wachstum von Software sollte durch konkrete Anforderungen motiviert sein. Doch was tun, nachdem ein Test definiert ist? Die Struktur des Produktionscodes ergibt sich ja nicht von allein, sobald man einen automatisierten Test hat.
Da greift das Buch bzw. die ganze Metapher bisher für mich zu kurz.
Heute morgen jedoch ist mir aufgegangen, wie das, was ich bisher als Softwareuniversum bezeichnet habe, eigentlich eine Anleitung zum Wachstum ist, also sozusagen die DNA für einen Softwareorganismus.
Eine ausführliche Beschreibung des Softwareuniversums finden Sie in meinem Buch The Architect’s Napkin - Der Schummelzettel. Im Weiteren benutze ich die Begriffe aus dem Softwareuniversum ohne ausführliche Erklärung. Ich möchte mich auf ihre Neuordnung konzentrieren.
Anforderungen als Nahrung
Getrieben wird das Wachstum von Software durch Anforderungen. Doch die sollten nicht allen in einen Topf geworfen werden. Eine differenzierte Sichtweise lohnt sich, denn unterschiedliche Arten von Anforderungen müssen beim Wachsen auch unterschiedlich behandelt werden.
Ich unterscheide drei Kategorien von Anforderungen:
Funktionale Anforderungen beschreiben, was Software tun soll, z.B. rechnen oder Auktionsangebote zugänglich machen. Qualitätsanforderungen geben vor, wie diese Funktionalität vollbracht werden soll, z.B. wie schnell, wie sicher usw.
Mit diesen Anforderungen werden Sie normalerweise durch den Kunden konfrontiert. Doch es gibt noch eine weitere Kategorie, die dem Kunden wichtig ist, auch wenn er meist dazu schweigt. Er hat nämlich auch noch Anforderungen im Hinblick auf die Investitionssicherheit seiner Software. Er möchte, dass Funktionalität und Qualität sich stets zügig wandeln lässt.
Wenn wir Software wachsen lassen wollen, müssen wir also darauf achten, dass sie in Richtung aller Anforderungen wächst. Und das systematisch. Und auch noch diskutierbar und kommunizierbar.
Wie das nur durch eine “guidance by tests” gehen soll, ist mir schleierhaft. Dem GOOS-Ansatz fehlt schlichtweg jedes Meta-Modell.
Der Softwarebaum
Anders sieht es aus, wenn ich die Dimensionen des Softwareuniversums in Form eines Baumes anordne:
Jetzt gibt es einen klaren Weg. Jetzt gibt es ein big picture. Jetzt gibt es “Wachstumsbausteine”.
Der Stamm
Am Anfang des Wachstums steht der Stamm. Hier liegen die Anforderungen unmittelbar an. Der Stamm wird im Dialog mit dem Kunden entwickelt. Das ist wichtig, denn auf ihm ruht am Ende die Krone mit all ihren Ästen.
Beim Stamm geht es darum, die Gesamtanforderungen immer feiner zu zerlegen in Inkremente. Das sind kundenrelevante Zuwächse in Form von Durchstichen. Zu denen kann der Kunde klares Feedback geben. Zu denen kann er natürlich auch Akzeptanzkriterien formulieren, die sich (hoffentlich) in automatisierte Tests übersetzen lassen.
Das größte solche Inkrement ist ein Bounded Context, das kleinste eine Interaktion.
Im Bild sehen Sie, dass der Stamm aus mehreren Ebenen besteht. Jede beschreibt ein anderes Abstraktionsniveau und besteht wiederum aus Artefakten der nächsten Ebene.
Der Funktionalitätsast
Aus dem Stamm entwickelt sich als erster Hauptast die Funktionalität. Für jede Interaktion des Stamms wird durch Zerlegung in einer Hierarchie von Datenflüssen bestimmt, wie das gewünschte Verhalten erzielt werden soll.
Im Bild steht, dass das zur Codierungszeit geschieht. Das ist insofern richtig, als dass dann mittels des Integration Operation Segregation Principle und des Principle of Mutual Oblivion die Herstellung von Datenflüssen gesichert werden muss. Aber natürlich sollen diese Flüsse vorher (in angemessenem Umfang) entworfen werden.
Die Länge und Verzweigungstiefe des Funktionalitätsastes ist beliebig. Er wird so ausladend, wie es nötig ist. Die Schachtelungstiefe seiner Funktionseinheiten ist unbegrenzt - wenn auch in der Praxis natürlich sehr endlich ;-) Die Knoten in diesem Teilbaum sind Integrationen, die Blätter Operationen.
Der Qualitätsast
Auch wenn Funktionalität das erste ist, was sich aus dem Stamm entwickeln soll, ist es weder das Einzige noch das Wichtigste. Software wird ja nicht für Funktionalität gemacht, sondern für Qualität.
Sobald also die Funktionalität soweit heruntergebrochen ist, dass sie sich in dünnen Inkrementen ausliefern lässt (Produktionseffizienz), und auch noch klar ist, was dann intern passieren muss, ist daran zu denken, wie die Qualitätsanforderungen eingehalten werden können. Es geht dann um Laufzeitcharakteristika wie Performance, Skalierbarkeit oder Robustheit.
Dazu sind andere Bausteine in den Blick zu nehmen. Bei der Funktionalität ging es im Wesentlichen um Funktionen, bei der Qualität geht es nun um Threads, Prozesse, Geräte usw. Die wiederkehrende Frage lautet: Wie sollten die Funktionen des funktionalen Entwurfs auf diese so genannten Hosts verteilt werden, um die geforderten Qualitäten herzustellen?
Der Wandelbarkeitsast
Last but not least das Thema Evolvierbarkeit. Auch wenn (oder gerade weil) der Kunde dazu nur eine diffuse Vorstellung hat, müssen Sie sich darum explizit beim Softwarewachstum kümmern.
Wenn klar ist, welche Funktionen die Funktionalität herstellen und wie die auf Hosts verteilt werden, ist zu überlegen, zu welchen so genannten Containern sie zusammengeschnürt werden sollen.
Welche Klassen, Bibliotheken, Komponenten, µServices sollte es geben, damit die Wandelbarkeit über lange Zeit erhalten bleibt?
Kohäsion und Kopplung sind die treibenden Kräfte bei der Containerfindung. Sie ist damit ein Thema der Entwurfszeit.
Wachstumsphasen
Der Softwarebaum wächst vom Stamm zu den Ästen. Die Reihenfolge dabei ist jedoch nicht so streng zu sehen, wie ich bisher vielleicht suggeriert habe. Je nach Umfang des zu entwickelnden Softwaresystems kann auf den Stamm auch der Qualitätsast folgen und dann erst der Funktionalitätsast. Der Evolvierbarkeitsast jedoch ist meist der letzte, der sprießt.
Doch auch das geschieht nicht nicht nur einmal, sondern wiederholt. Der Softwarebaum wächst in Phasen: ein bisschen Stamm, dann der Funktionalitätsast, dann der Qualitätsast, dann der Evolvierbarkeitsast - und schließlich geht es wieder von vorne los.
Wachstum ist mithin im Grunde immer überall am Werk. Stamm und Äste wachsen im Umfang und in der Breite.
Dass der Softwareorganismus nicht wie ein Schwamm wächst, sondern strukturierter, differenzierter, steckt im Meta-Modell. Das gilt, so denke ich, grundsätzlich für jedes Softwareprojekt. Denn die drei Anforderungskategorien sind ja universell. Und deren systematischer Bearbeitung dient die anatomische Grundstruktur des Softwarebaums. Keine Anforderung darf unter den Tisch fallen.
Auch wenn die endgültige Architektur einer Software über die Zeit emergieren mag, so sollte das nicht planlos geschehen. Tests können ruhig am Anfang jeder Umsetzung eines Inkrements stehen. Doch wie kommen Sie zu Inkrementen? Sie lassen sie nach dem Schema des Stamms wachsen. Und wie dann weiter, wenn die Tests für ein Inkrement definiert sind? Dann treiben Sie das Wachstum entlang der drei Äste Funktionalität, Qualität und Evolvierbarkeit weiter. Mit klaren Vorstellungen von Bausteinen und Regeln.
Softwareentwicklung ist keine Sache, die aus dem Handgelenk und intuitiv funktioniert. Da braucht es schon etwas mehr Systematik. Die liefert der Softwarebaum, finde ich. Er verbindet Klarheit und Struktur mit organischer Entwicklung.