Evolutionary Architecture: Systeme, die sich ändern dürfen
Architektur ist kein Endzustand, den man einmal festzurrt. Wer Wandel einplant und mit Fitness Functions absichert, verkürzt Zyklen und hält Qualität stabil – statt beides gegeneinander auszuspielen.
Die teuerste Architektur, die ich in Projekten sehe, ist nicht die schlecht geschnittene. Es ist die eingefrorene. Ein System, das vor drei Jahren sauber entworfen wurde, für eine Welt, die es so nicht mehr gibt – und das sich seither nicht mehr bewegt hat, weil jede Änderung zu riskant erschien. Die Diagramme hängen noch an der Wand, aber sie beschreiben ein Auto, das niemand mehr fährt.
In der Beratung begegnet mir dieselbe Grundannahme immer wieder: Architektur sei etwas, das man einmal richtig macht und dann verteidigt. Ein Zielbild, das man erreicht. Genau diese Annahme ist das Problem. Denn kein System, das produktiv genutzt wird, bleibt stehen. Anforderungen verschieben sich, Lastprofile ändern sich, Teams wachsen, Abhängigkeiten altern. Die Frage ist nie, ob sich ein System ändert, sondern nur, ob die Architektur diesen Wandel begleitet oder ihm im Weg steht.
Evolutionary Architecture dreht die Perspektive um. Statt Wandel als Ausnahme zu behandeln, die man nach dem Go-live möglichst vermeidet, macht sie ihn zur Norm, für die man von Anfang an baut. Neal Ford, Rebecca Parsons und Patrick Kua haben das Konzept in Building Evolutionary Architectures (O'Reilly 2017, zweite Auflage 2022) zusammengefasst: eine Architektur, die "guided, incremental change across multiple dimensions" unterstützt. Auf Deutsch, und weniger technisch: ein System, das sich ändern darf – geführt, in kleinen Schritten, und ohne dass dabei die Eigenschaften kaputtgehen, die es wertvoll machen.
Warum das Endzustands-Denken teuer wird
Der klassische Reflex heißt Big Design Up Front. Man denkt möglichst weit voraus, entscheidet früh alles Wichtige und gießt das Ergebnis in ein Zielbild. Das fühlt sich verantwortungsvoll an. Es ist auch nicht grundsätzlich falsch, früh nachzudenken – im Gegenteil. Falsch ist die Erwartung, dass diese frühen Entscheidungen dauerhaft tragen.
Der Denkfehler steckt im Zeitpunkt. Ganz am Anfang eines Vorhabens weiß man am wenigsten über die Domäne, die Nutzer und das reale Verhalten unter Last. Genau dann trifft man die weitreichendsten Entscheidungen. Jede spätere Erkenntnis muss sich dann gegen eine Struktur behaupten, die für sie nicht gemacht war. Das ist der Moment, in dem Teams anfangen, um die Architektur herumzuarbeiten statt mit ihr. Der Fachbegriff dafür ist architectural drift oder erosion: Das reale System entfernt sich Stück für Stück vom gedachten, bis das Diagramm an der Wand nur noch Folklore ist.
Der geschäftliche Schaden ist selten ein spektakulärer Ausfall. Er ist schleichend. Änderungen dauern länger. Jede Anpassung braucht mehr Abstimmung. Neue Features kollidieren mit alten Annahmen. Die Time-to-Market steigt, während die Zuversicht sinkt. Und irgendwann steht die Frage im Raum, ob man nicht besser alles neu bauen sollte – die teuerste aller Optionen.
Der Perspektivwechsel: geführte Evolution statt fixiertes Ziel
Der Ausweg ist kein anderes Zielbild, sondern ein anderer Umgang mit Zielbildern. Statt einen Endzustand zu definieren und zu verteidigen, definiert man die Eigenschaften, die das System behalten soll, und lässt die Struktur sich innerhalb dieser Leitplanken entwickeln.
flowchart LR
A[Change<br/>small increment] --> B[Deployment Pipeline]
B --> C{Fitness Functions<br/>guardrails}
C -->|pass| D[Learn & Adjust]
C -->|fail| E[Stop & Fix]
E --> A
D --> A
Der Kern ist ein Regelkreis, kein Wasserfall. Eine kleine Änderung läuft durch die Deployment-Pipeline, wird gegen definierte Leitplanken geprüft, liefert eine klare Rückmeldung – bestanden oder nicht – und daraus lernt das Team für den nächsten Schritt. Klein, geführt, wiederholbar. Das ist der Unterschied zwischen einem System, das sich entwickelt, und einem, das nur älter wird.
Wichtig ist die Betonung auf geführt. Evolutionary bedeutet ausdrücklich nicht "keine Architektur" oder "wir lassen es wachsen, wie es will". Das ist der häufigste Trugschluss. Wildwuchs ist keine Evolution, sondern ihr Gegenteil. Ohne bewusst benannte, priorisierte Architektur-Charakteristika gibt es nichts zu schützen und nichts zu messen. Der erste Schritt ist deshalb keine Technik, sondern eine Entscheidung: Was genau muss dieses System auszeichnen? Antwortzeit unter einer bestimmten Grenze? Lose Kopplung zwischen fachlichen Bereichen? Nachweisbare Sicherheit an den Rändern? Erst wenn diese Ziele explizit sind, kann man sie verteidigen.
Fitness Functions: Architektur, die man messen kann
Der Mechanismus, mit dem diese Ziele verteidigt werden, heißt Fitness Function. Der Begriff ist aus evolutionären Algorithmen entlehnt, wo eine Fitness-Funktion bewertet, wie nah eine Lösung am gewünschten Ergebnis ist. Übertragen auf Architektur heißt das laut den Autoren: "any mechanism that provides an objective integrity assessment of some architectural characteristic(s)". Also jeder Mechanismus, der objektiv prüft, ob eine architektonische Eigenschaft noch intakt ist.
Das Entscheidende an dem Wort ist objektiv. Eine Fitness Function ist kein Meeting, kein Bauchgefühl und kein Architecture Review Board, das einmal im Quartal tagt und über Slides urteilt. Sie ist ein automatisierter Test, der in der Pipeline läuft und ein eindeutiges Ergebnis liefert. Damit verschiebt sich Governance von manueller Kontrolle hin zu etwas Versioniertem, Ausführbarem, jederzeit Wiederholbarem – nicht umsonst trägt die zweite Auflage den Untertitel "Automating Software Governance".
Das einfachste und bekannteste Beispiel sind strukturelle Regeln. Mit einer Bibliothek wie ArchUnit lässt sich eine Architekturvorgabe als ganz normaler Unit-Test formulieren:
@ArchTest
static final ArchRule domain_must_not_depend_on_infrastructure =
noClasses().that().resideInAPackage("..domain..")
.should().dependOnClassesThat().resideInAPackage("..infrastructure..");
Diese Regel sagt: Die Domänenschicht darf nicht auf die Infrastruktur zugreifen. Bisher stand so etwas in einem Wiki und wurde in Code Reviews hoffentlich bemerkt. Jetzt bricht der Build, sobald jemand die Grenze verletzt – am selben Tag, an dem es passiert, nicht drei Monate später in einer Refactoring-Retrospektive. Die Absicht wird vom Kommentar zum ausführbaren Fakt.
Und genau hier liegt der zweite große Trugschluss, den ich in Projekten oft ausräumen muss: Fitness Functions sind nicht auf solche Package- und Layering-Checks beschränkt. Das ist nur die sichtbarste Kategorie. Alles, was man an einer Architektur objektiv prüfen kann, ist Kandidat – Performance-Budgets, Security-Scans, Prüfungen auf zyklische Abhängigkeiten, sogar Chaos-Experimente oder monitoring-basierte Prüfungen im laufenden Betrieb.
Die Autoren gliedern das in eine kleine Taxonomie, die im Alltag hilft, Lücken zu erkennen. Fitness Functions können atomic sein (prüfen eine einzelne Eigenschaft) oder holistic (prüfen das Zusammenspiel mehrerer). Sie können triggered sein (laufen bei einem Ereignis, etwa im Build) oder continual (laufen dauerhaft in Produktion). Und sie können static sein (feste Schwelle) oder dynamic (Schwelle abhängig vom Kontext). Wer nur strukturelle, atomic-triggered Regeln hat, deckt eine Ecke ab und übersieht den Rest.
Ein Performance-Budget etwa ist eher holistisch und lässt sich als Gate in der Pipeline formulieren:
fitness_functions:
- name: p95_latency_budget
metric: http_request_duration_p95_ms
threshold: 300
on_breach: fail_build
- name: max_module_coupling
metric: afferent_efferent_coupling_ratio
threshold: 0.7
on_breach: warn
Das Muster ist immer dasselbe: eine architektonische Eigenschaft, eine objektive Schwelle, eine definierte Konsequenz. Reißt die 95.-Perzentil-Latenz das Budget, bricht der Build. Steigt die Kopplung über den Grenzwert, gibt es zunächst eine Warnung. Man muss nicht alles hart erzwingen – aber man macht die Eigenschaft sichtbar und entscheidet bewusst, wie streng man sie behandelt.
Kopplung ist die eigentliche Bremse
Wenn ein System sich nicht mehr in kleinen Schritten ändern lässt, liegt das fast immer an Kopplung. Nicht nur an der offensichtlichen zwischen Klassen, sondern auch an versteckter zeitlicher Kopplung und an Datenkopplung. Zwei Dienste, die immer gemeinsam deployt werden müssen. Ein Schema, das drei Teams gleichzeitig anfassen müssen, um ein Feld zu ändern. Solange diese Bänder bestehen, ist inkrementeller Wandel unmöglich, egal wie gut die Absicht ist.
Deshalb gehört zu den wichtigsten Fitness Functions die Kontrolle der Kopplung selbst. Und deshalb sind ein paar bewährte Muster hier die eigentlichen Enabler. Die Strangler Fig Application von Martin Fowler beschreibt, wie man ein Altsystem inkrementell ablöst, indem man neue Funktionalität wie eine Würgefeige um den alten Kern wachsen lässt, bis der irgendwann verschwinden kann – ohne den einen großen, riskanten Umstieg. Auf Datenebene leistet das Expand-Contract-Muster (auch Parallel Change) dasselbe: Man erweitert ein Schema, migriert schrittweise, entfernt das Alte erst, wenn niemand es mehr braucht. Refactoring wird so zur normalen, sicheren Bewegung statt zum Kraftakt.
Auf der Betriebsebene sorgen Resilienz-Muster wie Circuit Breaker und Bulkhead aus Michael Nygards Release It! dafür, dass die lose gekoppelten Teile sich auch bei Störungen nicht gegenseitig mitreißen. Und an den Diensträndern hält Consumer-Driven Contract Testing die Grenzen ehrlich: Jeder Konsument beschreibt, was er von einem Dienst erwartet, und dieser Vertrag wird automatisiert geprüft. Ändert sich der Dienst inkompatibel, schlägt der Test an, bevor es der Nutzer merkt. Auch das ist eine Fitness Function – eine, die Änderbarkeit an den teuersten Bruchstellen absichert.
Ein Wort zu Conway's Law, denn Kopplung ist nicht nur technisch. Systeme spiegeln die Kommunikationsstrukturen der Organisationen, die sie bauen. Wer lose gekoppelte Software will, muss auch die Teams entsprechend schneiden. Das ist keine Randnotiz, sondern oft die Voraussetzung dafür, dass die technischen Leitplanken überhaupt greifen.
Der Beweis, dass es sich rechnet
An dieser Stelle kommt der Einwand, den ich in jedem zweiten Gespräch höre: Klingt gut, aber all diese Automatisierung und dieses ständige Ändern – geht das nicht auf Kosten der Stabilität? Schneller heißt doch riskanter?
Die Daten sagen das Gegenteil. Die DORA-Forschung, zusammengefasst in Accelerate (Forsgren, Humble, Kim, 2018), zeigt über tausende Organisationen hinweg, dass Geschwindigkeit und Stabilität keine Gegner sind, sondern gemeinsam steigen. Die leistungsstärksten Teams deployen häufiger und haben seltener Ausfälle und erholen sich schneller. Der vermeintliche Zielkonflikt zwischen Tempo und Verlässlichkeit ist ein Artefakt des alten Denkens – das Ergebnis von seltenen, großen, riskanten Releases.
Die vier klassischen DORA-Metriken lesen sich selbst wie systemweite Fitness Functions:
Deployment Frequency - wie oft deployen wir?
Lead Time for Changes - wie lange von Commit bis Produktion?
Change Failure Rate - wie viele Deployments verursachen einen Fehler?
Time to Restore Service - wie schnell sind wir wieder online?
Sie messen nicht ein einzelnes Modul, sondern die Änderbarkeit des Gesamtsystems. Verkürzte Zyklen, weniger Reibung, höhere Qualität – nicht als Slogan, sondern als messbare Größen, die man in dieselbe Regelkreis-Logik einhängen kann wie eine Latenzgrenze.
flowchart TB
subgraph Old[Architecture as end-state]
O1[Big Design Up Front] --> O2[freeze] --> O3[drift & erosion]
end
subgraph New[Architecture as guarded evolution]
N1[named characteristics] --> N2[fitness functions] --> N3[small guarded change] --> N1
end
Der Denkrahmen ist im Übrigen nicht auf Technik beschränkt. Er ist dasselbe Prinzip, das The Lean Startup für Produkte formuliert hat – Build, Measure, Learn: eine Hypothese, ein kleiner Schritt, eine Messung, eine Anpassung. Architektur-Evolution ist die technische Ausprägung derselben Haltung. Man behauptet nicht, die richtige Struktur zu kennen. Man baut so, dass man es günstig herausfinden kann.
Fazit
Architektur ist kein Endzustand. Sie ist die Fähigkeit eines Systems, sich weiter verändern zu lassen, ohne die Eigenschaften zu verlieren, die es wertvoll machen. Wer das ernst nimmt, hört auf, ein Zielbild zu verteidigen, und fängt an, die richtigen Dinge zu schützen.
Der Weg dahin ist erstaunlich konkret. Erstens: die Architektur-Charakteristika benennen, die wirklich zählen – nicht alle, sondern die wenigen, die den Unterschied machen. Zweitens: für jede dieser Eigenschaften eine Fitness Function bauen, die objektiv, automatisiert und in der Pipeline prüft, ob sie noch hält – strukturell, aber eben auch bei Performance, Sicherheit und Kopplung. Drittens: Kopplung aktiv niedrig halten, technisch wie organisatorisch, damit kleine Schritte überhaupt möglich bleiben. Und viertens: dem Ganzen vertrauen, weil die Daten zeigen, dass Tempo und Stabilität zusammen wachsen.
Das Ergebnis ist kein aufgeblähter Prozess, sondern das Gegenteil: weniger Reibung, kürzere Zyklen, mehr Zuversicht bei jeder Änderung. Ein System, das sich ändern darf, ist am Ende einfacher und eleganter als eines, das man mit immer mehr Vorsicht verteidigen muss. Die beste Architektur ist nicht die, die für immer stimmt. Es ist die, die stimmen bleiben darf, während sich alles andere bewegt.
Weiterführende Quellen
- Neal Ford, Rebecca Parsons, Patrick Kua: Building Evolutionary Architectures, O'Reilly, 1. Aufl. 2017 / 2. Aufl. 2022 (mit Pramod Sadalage)
- Martin Fowler: Foreword to Building Evolutionary Architectures
- DORA – Four Key Metrics
- Nicole Forsgren, Jez Humble, Gene Kim: Accelerate, IT Revolution, 2018
- ArchUnit – Unit-Tests für Architekturregeln
- InfoQ – Fitness Functions for Your Architecture
- Michael Nygard: Release It!, Pragmatic Bookshelf, 2. Aufl. 2018
Kommentare