Definition of Done als Qualitäts-Gate, nicht als Bürokratie
Eine Definition of Done wirkt nur, wenn sie ausführbar ist: ein grünes Gate aus Lint, Build und Test statt einer Checkliste, die im Review-Meeting abgehakt wird.
In fast jedem Team, das ich als Berater begleite, gibt es sie: die Definition of Done. Meistens klebt sie ausgedruckt neben dem Board, hat zwölf Punkte, und niemand liest sie mehr. Sie ist zu einem Ritual geworden. Am Ende des Sprints geht jemand die Liste durch, hakt ab, und alle nicken. Ob die Software wirklich fertig ist, weiß danach niemand besser als vorher.
Ich habe eine klare Meinung dazu, und die ist der rote Faden dieses Artikels: Eine Definition of Done entfaltet ihren Wert erst, wenn sie ausführbar ist. Nicht als Word-Dokument, sondern als Kommando. Nicht als Meeting, sondern als Gate. Der Kern ist grün oder rot – Lint, Build und Tests laufen durch oder sie tun es nicht. Alles, was sich so automatisieren lässt, gehört automatisiert. Der Rest, der menschliches Urteil braucht, bleibt ehrlich als solcher benannt und wird nicht so getan, als wäre er Code.
Das klingt zunächst nach einer technischen Verschiebung. Tatsächlich ist es eine über das Geschäftsergebnis: kürzere Zyklen, weniger Reibung im Team, höhere und vor allem verlässlichere Qualität. Wer Qualität an ein automatisiertes Gate koppelt, streitet nicht mehr im Review darüber, ob etwas fertig ist. Die Pipeline sagt es.
Warum das Ritual scheitert
Die klassische Definition of Done als Checkliste hat ein strukturelles Problem: Sie prüft zum spätestmöglichen Zeitpunkt und mit dem unzuverlässigsten Prüfinstrument, das wir haben – einem müden Menschen am Sprintende. Wenn ein Punkt lautet "alle Tests grün", dann bedeutet das Abhaken dieses Punkts nicht, dass die Tests grün sind. Es bedeutet, dass jemand glaubt, dass sie es sind, oder es zumindest behauptet.
Genau hier entsteht die Ticket-Schlacht, gegen die ich argumentiere. Weil die Checkliste nicht bindet, wird der Streit ins Soziale verlagert. Ist das jetzt "done" oder "done done"? Zählt das Refactoring noch dazu? Hat der Product Owner das schon abgenommen? Jede dieser Fragen kostet Zeit, erzeugt Reibung und – das ist das Schlimmste – vermittelt eine Sicherheit, die es gar nicht gibt. Ein abgehakter Punkt fühlt sich an wie eine Garantie, ist aber nur eine Absichtserklärung.
Martin Fowler hat den technischen Gegenentwurf schon lange vorher beschrieben. Selbsttestender Code und Continuous Integration bilden ein Fundament, in dem jeder Commit einen automatischen Build- und Testlauf auslöst. Ein roter Build ist ein Stopp – "stop the line". Das ist kein Vorschlag, über den man diskutieren kann. Es ist ein Zustand. Genau diese Eigenschaft will ich für die Definition of Done: Sie soll ein Zustand sein, kein Konsens.
Die Definition of Done als Commit-Stage
Der entscheidende gedankliche Schritt kam für mich über die Deployment Pipeline von Jez Humble und David Farley. Ihr Modell macht Qualität zu einem automatisierten Durchlauf. Die erste Stufe, die Commit-Stage, kompiliert den Code, führt Unit-Tests aus, lässt Linter und statische Analyse laufen. Erst wenn diese Stage grün ist, starten spätere Stufen überhaupt. Alles andere ist blockiert.
Wenn ich das ernst nehme, dann lässt sich eine Definition of Done fast eins zu eins auf diese Commit-Stage abbilden. Was in der Checkliste stand, wird zu Schritten in der Pipeline:
commit_stage:
fail_fast: true # any red step blocks the merge
steps:
- name: compile
- name: lint # static analysis, style rules
- name: unit_test
- name: coverage_check # fail below an agreed threshold
- name: contract_test # verify integration boundaries
gate:
rule: merge_blocked_unless(commit_stage == "green")
Das Interessante an dieser Darstellung ist, was sie erzwingt. Ein Punkt, den ich nicht als Schritt formulieren kann, gehört nicht in den automatisierten Teil der Definition of Done. Ich kann "code compiles" ausführen. Ich kann "no lint errors" ausführen. Ich kann "coverage above threshold" ausführen. Ich kann "der Code ist elegant" nicht ausführen – das ist ein menschliches Urteil und bleibt im Review, wo es hingehört.
Diese Trennschärfe ist der eigentliche Gewinn. Die Definition of Done zerfällt in zwei sauber getrennte Teile: ein hartes, ausführbares Gate und einen kleinen, ehrlich benannten Rest aus menschlichen Prüfungen. Kein Vermischen mehr, keine Scheingenauigkeit.
Ein Kommando, lokal und in der CI
Damit das Gate im Alltag trägt, braucht es eine Eigenschaft, die oft übersehen wird: Der Entwickler muss es lokal genauso ausführen können wie die CI. Wenn die Pipeline andere Dinge prüft als das, was ich vor dem Push laufen lassen kann, dann verliere ich den schnellen Rückkopplungskreis und bekomme rote Builds erst nach dem Push. Das ist Reibung, und sie ist vermeidbar.
Deshalb bündele ich die ausführbare Definition of Done in genau einem Target. Der Name ist zweitrangig, die Idee zählt:
# One command is the executable Definition of Done.
# Developers run it before push; CI runs the exact same target.
verify: lint build test
@echo "Definition of Done: green"
lint:
eslint . --max-warnings 0
build:
npm run build
test:
npm test -- --coverage
Der Merge-Gate in der CI ruft kein eigenes Skript auf. Er ruft dasselbe verify. Damit gilt: Was lokal grün ist, ist auch in der CI grün, und umgekehrt. Die Kernbotschaft, die ich Teams mitgebe, passt in einen Satz: Die Definition of Done ist ein Kommando, kein Dokument.
Als Git-Hook wird daraus ein leichtes Sicherheitsnetz, das den langsamen Weg über die CI gar nicht erst nötig macht:
#!/usr/bin/env sh
# .git/hooks/pre-push
# Run the executable DoD before anything leaves the machine.
if ! make verify; then
echo "Push blocked: Definition of Done is red."
exit 1
fi
Man sieht sofort, warum das die Zyklen verkürzt. Der Entwickler bekommt das Rot in Sekunden auf der eigenen Maschine, nicht Minuten später aus der Pipeline und schon gar nicht Tage später im Sprint-Review. Fehler werden dort gefangen, wo sie am billigsten zu beheben sind: direkt nach ihrer Entstehung.
Der Weg durch das Gate
Ich stelle die beiden Welten gern nebeneinander, weil der Unterschied dann jeder im Team sofort sieht. Auf der einen Seite das Ritual, auf der anderen das Gate:
flowchart TD
A[Commit] --> B{Ritual oder Gate?}
B -->|Ritual| C[12-Punkte-Checkliste]
C --> D[manuelles Abhaken]
D --> E[Review-Meeting]
E --> F[Merge<br/>Hoffnung, kein Beweis]
B -->|Gate| G[Commit Stage:<br/>lint, build, test, contract]
G --> H{alles grün?}
H -->|ja| I[Merge]
H -->|nein| J[blockiert<br/>zurück zum Entwickler]
Der linke Pfad endet in Hoffnung. Der rechte endet in einem Beweis oder in einer klaren Blockade. Und die Blockade ist kein Vorwurf an eine Person, sondern eine sachliche Rückmeldung des Systems. Das entlastet auch die Teamdynamik enorm – nicht ein Kollege sagt "das ist nicht fertig", sondern das Gate sagt es, reproduzierbar und für alle gleich.
Empirisch stützt sich diese Haltung auf die State-of-DevOps-Reports von 2014 und 2015, an denen unter anderem Nicole Forsgren, Gene Kim und Jez Humble mitgearbeitet haben. Ihr Befund widerlegt den verbreiteten Reflex, Tempo gehe auf Kosten der Qualität. High-Performer koppeln hohe Deploy-Frequenz mit niedriger Change-Fail-Rate. Tempo und Qualität sind kein Trade-off – vorausgesetzt, die Prüfungen laufen automatisiert statt manuell. Genau das ist der Business-Case für das ausführbare Gate: Es macht schnell und sicher zugleich möglich.
Auch Grenzen lassen sich als Gate prüfen
Ein häufiger Einwand: Unit-Tests prüfen nur die eigene Einheit, aber die richtig teuren Fehler entstehen an den Integrationsgrenzen zwischen Services. Stimmt. Und auch dafür gibt es einen ausführbaren Weg statt eines Abnahme-Meetings. Consumer-Driven Contracts, wie Fowler und Ian Robinson sie beschrieben haben und wie Pact sie handhabbar macht, verwandeln die Frage "passen unsere Schnittstellen zusammen?" in ein grün-rot-Signal.
// A consumer-driven contract turns an integration
// boundary into an executable check.
provider
.given("an order with id 42 exists")
.uponReceiving("a request for order 42")
.withRequest({ method: "GET", path: "/orders/42" })
.willRespondWith({
status: 200,
body: { id: 42, status: "shipped" }
});
Der Contract-Test wird Teil derselben Commit-Stage. Damit wandert eine weitere Prüfung, die früher in Meetings und Abstimmungsketten stattfand, in die Pipeline. Weniger Termine, mehr Beweise. Das ist die Richtung, in die ich Teams immer wieder schiebe: menschliche Koordination durch ausführbare Übereinkünfte ersetzen, wo es geht.
Ehrlich bleiben: was das Gate nicht kann
An dieser Stelle muss ich gegen mein eigenes Argument arbeiten, sonst wird es unredlich. Ein grünes Gate ist nicht dasselbe wie Qualität. Es ist der Nachweis, dass die vereinbarten Prüfungen ausgeführt wurden und durchgelaufen sind – nicht der Nachweis, dass diese Prüfungen aussagekräftig sind. Eine hohe Coverage mit schwachen Assertions ist grün und trotzdem wertlos. Tests, die nichts wirklich behaupten, geben eine Scheinsicherheit, die gefährlicher ist als offen zugegebene Unsicherheit.
Das Gate misst also die Ausführung von Prüfungen, nicht deren Güte. Die Güte der Prüfungen selbst bleibt eine menschliche, fachliche Verantwortung. Wer das verwechselt, ersetzt das alte Checklisten-Ritual nur durch ein Coverage-Ritual.
Ebenso ehrlich: Nicht jeder Punkt einer Definition of Done ist automatisierbar. "Dokumentation aktualisiert", "vom Product Owner abgenommen", "Barrierefreiheit manuell geprüft" – das bleibt menschlich. Der Fehler wäre, so zu tun, als würde alles zu Code. Der richtige Umgang ist Trennung: das automatisierbare Kern-Gate hart und blockierend machen, den manuellen Rest klein, sichtbar und bewusst als menschliche Entscheidung stehen lassen.
Und noch eine Abgrenzung, die in der Praxis viel Reibung spart: Die Definition of Done ist nicht dasselbe wie Akzeptanzkriterien. Die Definition of Done ist projektweit und stabil, ein Qualitätsstandard, der für jede Story gilt. Akzeptanzkriterien sind pro Story, sie beschreiben das fachlich Gewollte dieser einen Anforderung. Wer beides vermischt, bekommt genau die aufgeblähten, ständig neu verhandelten Listen zurück, gegen die dieser Artikel geschrieben ist.
Fazit
Die Definition of Done ist eine gute Idee, die in vielen Teams an ihrer Form scheitert. Als Checkliste am spätesten Punkt geprüft, von einem müden Menschen abgehakt, erzeugt sie Reibung und Scheinsicherheit statt Qualität. Als ausführbares Gate ist sie das Gegenteil: ein Kommando, das lokal und in der CI dasselbe prüft, das Fehler früh und billig fängt und das nicht diskutiert wird, sondern gilt.
Der Weg dorthin ist konzeptionell einfach und genau deshalb elegant. Bilde den automatisierbaren Kern der Definition of Done auf die Commit-Stage einer Pipeline ab. Bündele ihn in einem verify-Kommando. Blockiere den Merge, solange es rot ist. Trenne diesen harten Kern sauber vom kleinen manuellen Rest und tu nicht so, als wäre alles Code. Und bleib ehrlich darüber, dass grün "geprüft" heißt, nicht "gut".
Das Geschäftsergebnis dieser Verschiebung ist real und messbar. Kürzere Feedback-Zyklen, weniger Meetings über den Zustand des Codes, weniger soziale Reibung im Team und eine Qualität, die nicht auf Hoffnung beruht, sondern auf einem reproduzierbaren Zustand. Definition of Done wird vom Bürokratie-Ritual zum stillen, verlässlichen Türsteher. Und der beste Türsteher ist der, über den niemand mehr reden muss.
Weiterführende Quellen
- Martin Fowler: Continuous Integration und SelfTestingCode, martinfowler.com
- Jez Humble, David Farley: Continuous Delivery, 2010 – Deployment Pipeline und Commit-Stage
- Puppet Labs: State of DevOps Report 2014 und 2015 (Forsgren, Kim, Humble)
- Ken Schwaber, Jeff Sutherland: The Scrum Guide – Definition of Done
- Martin Fowler, Ian Robinson: Consumer-Driven Contracts, martinfowler.com; Tool: Pact
- Michael Nygard: Release It!, 2007 – Stability Patterns als nicht-funktionale Prüfziele
Kommentare