# Definition of Done als Qualitäts-Gate, nicht als Bürokratie

URL: https://www.mikebild.dev/de/blog/definition-of-done-als-qualitaets-gate/

```console
$ make verify
lint    ✓  0 warnings
build   ✓
test    ✗  2 failing, coverage 61 % (threshold 80 %)

Definition of Done: rot
```

Dieses Rot lässt sich nicht verhandeln. Niemand kann es abhaken, niemand muss es im Review interpretieren, und jeder im Team reproduziert es in Sekunden auf der eigenen Maschine. Die ausgedruckte Zwölf-Punkte-Liste neben dem Board leistet das nicht. Steht dort "alle Tests grün" und setzt jemand ein Häkchen, dann ist damit nicht gesagt, dass die Tests grün sind – nur, dass jemand es glaubt oder behauptet.

An diesem Unterschied hängt alles Weitere. Was sich an einer Definition of Done als Kommando formulieren lässt, gehört in ein Gate, das den Merge blockiert, solange es rot ist. Was menschliches Urteil braucht, bleibt ehrlich als solches benannt, klein und sichtbar – und wird nicht so behandelt, als wäre es Code.

Das klingt zunächst nach einer rein technischen Verschiebung. Tatsächlich schlägt sie bis ins Geschäftsergebnis durch: 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 beginnt die Ticket-Schlacht. 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:

```yaml
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 den ausführbaren Kern der Definition of Done in genau einem Target. Der Name ist zweitrangig, die Idee zählt:

```makefile
# 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
```

Das Merge-Gate in der CI ruft kein eigenes Skript auf. Es ruft dasselbe `verify`. Damit gilt: Was lokal grün ist, ist auch in der CI grün, und umgekehrt. In einem Satz: Der automatisierbare Teil der Definition of Done ist ein Kommando; die vollständige Vereinbarung bleibt mehr als dieses Kommando.

Als Git-Hook wird daraus ein leichtes Sicherheitsnetz, das den langsamen Weg über die CI gar nicht erst nötig macht:

```bash
#!/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

Nebeneinandergestellt springt der Unterschied ins Auge. Auf der einen Seite das Ritual, auf der anderen das Gate:

```mermaid
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 Signal, das grün oder rot ist.

```javascript
// 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. Der Hebel liegt überall dort, wo Abstimmung nur deshalb nötig ist, weil niemand die Übereinkunft ausführen kann – solche Koordination lässt sich durch ausführbare Übereinkünfte ersetzen.

## 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. Rein mechanisch als Checkliste am spätesten Punkt abgehakt, erzeugt sie Reibung und Scheinsicherheit statt Qualität. Ihr automatisierbarer Kern gehört deshalb in ein Gate, das lokal und in der CI dasselbe prüft. Gemeinsame Qualitätskriterien und fachliche Abnahme verschwinden dadurch nicht; sie werden nur nicht als automatischer Beweis ausgegeben.

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
