Einfach schlagen: die elegante Lösung braucht weniger Prozess
Prozess-Overhead ist oft nur ein Symptom: Wer das Problem kleiner schneidet und einfacher entwirft, braucht am Ende weniger Koordination, weniger Tickets und weniger Meetings.
Wenn ich in ein Team komme, das über zu viel Prozess klagt, höre ich fast immer dieselben Sätze. Wir brauchen noch ein Change Board. Wir müssen die Freigaben straffen. Wir sollten das Ticket-Board umbauen. Und fast immer ist meine erste Frage eine andere: Warum ist die Änderung, um die es geht, eigentlich so groß, dass sie drei Teams, zwei Reviews und ein Approval-Meeting braucht?
Denn das ist meine These aus vielen Beratungsprojekten – und sie klingt unbequemer, als sie gemeint ist: Prozess-Overhead ist oft ein Symptom fehlender Einfachheit im Entwurf. Nicht immer, aber oft. Wer das Problem kleiner schneidet und die Lösung schlichter baut, braucht am Ende weniger Koordination, weniger Tickets, weniger Meetings. Der Prozess, der die Reibung auffangen sollte, wird dann schlicht überflüssig.
Overhead ist teuer, bevor er sichtbar wird
Bevor ich zum Entwurf komme, lohnt ein Blick auf die Rechnung. Martin Fowler hat in seinem Text zu YAGNI ("You Aren't Gonna Need It") vorgezogene, spekulative Komplexität sauber in Kostenarten zerlegt: Es gibt build cost – das Bauen des Features, das man heute nicht braucht. Es gibt delay cost – das, was man in derselben Zeit nicht am eigentlichen Problem gearbeitet hat. Es gibt carry cost – jede Zeile spekulative Flexibilität muss ab sofort mitgetragen, verstanden, getestet und umgangen werden. Und es gibt repair cost, wenn die vorgebaute Annahme sich als falsch herausstellt.
Der Punkt, der in Diskussionen fast immer untergeht: Ein großer Teil dieser Kosten ist Koordinationskosten. Ein flexibler, konfigurierbarer, "für später" ausgelegter Entwurf braucht ein Design Review. Er braucht ein Config-Ticket. Er kreuzt Teamgrenzen und braucht deshalb Abstimmung. YAGNI ist deshalb kein Faulheitsprinzip, sondern ein Designprinzip – und nebenbei ein Prozessspar-Prinzip. Weniger vorgebaute Komplexität heißt weniger, worüber man sich abstimmen muss.
Ein kleines Beispiel, das ich in Varianten immer wieder sehe:
# Over-engineered: a pluggable strategy, a factory, and a config object
# for ONE rule that exists today. This needs a design review, a config
# ticket, and a feature-flag rollout before it ships.
class DiscountEngine:
def __init__(self, strategy: DiscountStrategy, config: DiscountConfig):
self.strategy = strategy
self.config = config
def apply(self, price: Money, customer: Customer) -> Money:
return self.strategy.compute(price, customer, self.config)
# YAGNI: the one rule you actually have today. One PR, no coordination,
# no ticket for a config value nobody has asked for yet.
def apply_discount(price: Money, is_returning_customer: bool) -> Money:
return price * 0.9 if is_returning_customer else price
Die untere Variante ist nicht "schludrig". Sie ist ehrlich. Sie beschreibt exakt die eine Regel, die es heute gibt. Und sie kostet in der Auslieferung fast nichts – keine Runde durchs Gremium, kein Flag, kein Meeting. Wenn morgen eine zweite Discount-Regel dazukommt, refactore ich – dann, mit echter Information, statt heute auf Verdacht.
Der eigentliche Hebel: Schnittgröße
Der Discount ist die kleine Bühne. Die große ist die Frage, wie groß eine Änderung ist, bevor sie live geht. Und genau hier steckt der Zusammenhang, den ich in Reviews am liebsten aufmale.
flowchart TB
A["One large change<br/>crossing three teams"] --> B[Design Review]
B --> C[Cross-team Ticket]
C --> D[Merge Conflict]
D --> E[Change Board]
E --> F[Coordination Meeting]
F --> G[Release]
A2["Same value, cut into three<br/>small independent changes"] --> H["Context A<br/>own pipeline"]
A2 --> I["Context B<br/>own pipeline"]
A2 --> J["Context C<br/>own pipeline"]
H --> K[Release]
I --> K
J --> K
Die Kernaussage der Grafik ist unbequem einfach: Der Koordinationsaufwand hängt nicht am Feature-Wert. Er hängt an der Schnittgröße und daran, wie viele Teamgrenzen ein Schnitt kreuzt. Ein großer Change, der drei Teams berührt, erzeugt dicke Koordinationskanten – Reviews, Merge-Konflikte, Change Board, Abstimmungsrunden. Derselbe fachliche Wert, in drei kleine, unabhängige Änderungen zerlegt, erzeugt fast keine Querkanten. Jede Änderung lebt in ihrem eigenen Bounded Context, geht durch ihre eigene Pipeline.
Das ist keine Theorie. Die State-of-DevOps-Reports der letzten Jahre – die Arbeit rund um Forsgren, Humble und Kim – zeigen dasselbe empirisch. High Performer arbeiten mit wenigen aktiven Branches, integrieren mindestens einmal täglich in den Trunk und kennen keine langen Integrations- oder Freeze-Phasen. Sie ersetzen Merge- und Koordinations-Overhead durch kleine Batches. Trunk-Based Development ist genau das: den Schnitt so klein machen, dass die aufwendige Koordination gar nicht erst entsteht.
Und der Teil, der Skeptiker überrascht: Kleine Deployments sind nicht riskanter, sondern sicherer. Dieselben Daten zeigen, dass hohe Deployment-Frequenz und kurze Lead Time for Changes mit einer niedrigeren Change Fail Rate und kürzerer Wiederherstellungszeit korrelieren. Ein kleiner Schnitt ist leichter zu verstehen, leichter zu reviewen und im Fehlerfall leichter zurückzudrehen. Ein großer Schnitt braucht die schwere Approval-Zeremonie – weil er sie tatsächlich verdient hat. Man baut sich den Prozess, den man durch die Größe erzwingt.
Wenn Koordination bloß Symptom ist
Der klassische Fall, der mir begegnet: Zwei Teams stimmen sich in einem wiederkehrenden Integrations-Meeting ab, weil Team A einen Endpunkt bereitstellt, von dem Team B abhängt. Jede Änderung wird vorher besprochen, jede Integration gemeinsam getestet. Der Prozess ist real – aber er kompensiert nur, dass niemand die Erwartung zwischen den Teams explizit gemacht hat.
Consumer-Driven Contracts drehen das um. Der Consumer beschreibt genau, was er braucht – nicht mehr. Der Provider verifiziert diesen Vertrag in seiner eigenen Pipeline, unabhängig. Das Abstimmungs-Meeting verschwindet, weil die Abstimmung im Code steht:
// The consumer declares only what it actually needs. No cross-team
// integration meeting, no shared end-to-end test suite to schedule.
pact
.given("customer 42 exists")
.uponReceiving("a request for the customer name")
.withRequest({ method: "GET", path: "/customers/42" })
.willRespondWith({
status: 200,
body: { name: like("Ada") },
});
// The provider replays this contract in ITS OWN pipeline and fails fast
// if it ever breaks the expectation — independently, no meeting required.
Werkzeuge wie Pact machen das seit einigen Jahren praktikabel. Der Effekt ist derselbe wie beim kleinen Schnitt: Ein Stück Koordination, das vorher als Meeting existierte, wird zu ausführbarer Spezifikation – und der Prozess drumherum ist nicht mehr nötig.
Governance ohne Gremium
Dasselbe Muster gilt auf Architekturebene. Viele schwergewichtige Governance-Prozesse – das Architektur-Gremium, das jede Änderung absegnet – existieren, weil niemand eine leichtgewichtigere Art gefunden hat, Architekturregeln durchzusetzen. Neal Ford und Rebecca Parsons prägen für die Alternative einen schönen Begriff: fitness functions. Statt einmal groß vorab zu entwerfen und dann per Gremium zu bewachen, formuliert man die Architekturziele als automatisch prüfbare Regeln in der Deployment-Pipeline.
# Architecture rules as executable guardrails in the pipeline,
# not as a slide reviewed once a quarter by a committee.
fitness_functions:
- name: modules_stay_decoupled
rule: "no cyclic dependencies between packages"
- name: p95_latency_budget
threshold_ms: 300
- name: public_api_is_stable
rule: "no breaking change without a new version"
Ist die Regel verletzt, wird der Build rot – sofort, nachvollziehbar, für alle gleich. Das ersetzt keine Architekturarbeit; es ersetzt das Ritual, mit dem man sie sonst durchsetzt. Verwandt dazu sind Fowlers Ideen der "Sacrificial Architecture" und des "MonolithFirst": erst einfach und notfalls wegwerfbar bauen, Komplexität nur dann einziehen, wenn die Realität sie erzwingt. Evolutionäre Architektur ist die konsequente Weigerung, Prozess-Overhead für eine Zukunft zu bezahlen, die man noch nicht kennt.
Wo die These an ihre Grenze kommt
Ich würde diesen Text nicht schreiben, wenn ich nicht auch die Gegenrede kennen würde. Drei ehrliche Einschränkungen, damit aus einer guten Heuristik keine Ideologie wird.
Erstens: Einfach ist nicht dasselbe wie simplistisch. YAGNI gilt für spekulative Features und vorgezogene Flexibilität – nicht für innere Qualität. "Wir lassen die Tests und das Design weg, dann sind wir schneller" ist die Fehllesart, gegen die Fowler ausdrücklich anschreibt. Der Unterschied ist der zwischen accidental und essential complexity, den schon Brooks gezogen hat. Weniger spekulative Struktur: ja. Weniger Sorgfalt im Kern: nein. Die kleine Discount-Funktion von oben braucht ihren Test genauso wie die große Engine – nur eben genau einen, für die eine Regel, die es gibt.
Zweitens: Overhead ist nicht immer Design-Verschulden. Compliance-Anforderungen sind real. Regulatorische Gates sind real. Echte Abhängigkeiten zwischen Teams, die sich nicht wegschneiden lassen, sind real. Deshalb sage ich bewusst "oft", nicht "immer". Meine Aufforderung ist nicht, jeden Prozess abzuschaffen, sondern bei jedem Prozessschritt zu fragen: Kompensierst du gerade eine Anforderung von außen – oder eine Komplexität, die wir uns selbst gebaut haben?
Drittens: Ein kleinerer Schnitt kann Komplexität auch nur verschieben. Aus einem Modul zwei Services zu machen tauscht Code-Kopplung gegen verteilte Kopplung und Netzwerk ein. Das ist manchmal richtig, aber es ist nie gratis. Genau darum sind Contract Tests der Ersatz für den vollen End-to-End-Integrationszirkus – und genau darum lohnt Fowlers "MicroservicePremium" als Merksatz: Verteilung muss man sich verdienen, sie ist kein Default. Wer klein schneidet, ohne den Schnitt entlang echter fachlicher Grenzen zu legen, hat am Ende mehr Koordination, nicht weniger.
Wie ich es konkret angehe
Wenn ich mit einem Team an genau dieser Stelle arbeite, ist die Reihenfolge fast immer dieselbe – und sie beginnt nicht beim Prozess.
flowchart LR
A["Spot the heavy<br/>process step"] --> B["Ask: what complexity<br/>is it compensating?"]
B --> C["Cut the change<br/>smaller"]
C --> D["Make the expectation<br/>explicit as a contract"]
D --> E["Turn the gate into a<br/>fitness function"]
E --> F["Drop the step<br/>that is now redundant"]
Zuerst suche ich den teuersten Prozessschritt – das Meeting, das alle nervt, das Approval, das immer blockiert. Dann frage ich nicht "wie machen wir es effizienter", sondern "welche Komplexität fängt es auf". Meistens finden wir dahinter einen zu großen Schnitt, eine unausgesprochene Erwartung oder eine Governance, die niemand automatisiert hat. Dann schneiden wir kleiner, machen die Erwartung als Contract explizit, gießen das Gate in eine fitness function – und lassen den Schritt, der jetzt nichts mehr auffängt, ersatzlos weg.
Das Ergebnis ist selten spektakulär und fast immer spürbar: kürzere Zyklen, weil weniger auf Freigaben wartet. Weniger Reibung, weil weniger über Teamgrenzen abgestimmt werden muss. Höhere Qualität, weil kleine Änderungen leichter zu prüfen und im Fehlerfall leichter zurückzudrehen sind. Einfach und elegant statt Prozess-Overhead – nicht als Sparmaßnahme, sondern als Folge eines besseren Entwurfs.
Fazit
Prozess ist teuer, und meistens ist er teuer aus gutem Grund: Er fängt Reibung auf, die real da ist. Aber die interessantere Frage ist fast nie, wie man den Prozess effizienter macht. Sie lautet, warum die Reibung überhaupt entsteht. Sehr oft ist die Antwort ein Entwurf, der zu groß, zu spekulativ oder zu implizit ist.
Kleiner schneiden schlägt mehr koordinieren. Explizite Verträge schlagen wiederkehrende Abstimmung. Automatisierte Guardrails schlagen Gremien. Und YAGNI schlägt jede Flexibilität, die man heute baut und morgen umgeht. Das ist keine Aufforderung zur Schludrigkeit – innere Qualität bleibt nicht verhandelbar. Es ist die Einladung, den Prozess-Overhead nicht als Naturgesetz zu nehmen, sondern als Symptom zu lesen. Wer das Problem an der richtigen Stelle klein macht, braucht die schwere Maschinerie drumherum gar nicht erst.
Einfach schlagen heißt genau das: die elegantere Lösung ist die, die weniger Prozess braucht – weil sie das Problem gelöst hat, das der Prozess sonst hätte verwalten müssen.
Kommentare