post

Architecture Decision Records: Entscheidungen, die man in zwei Jahren noch versteht

Warum wir uns damals für PostgreSQL und gegen den Message-Bus entschieden haben? ADRs machen das Warum als versioniertes Markdown-Artefakt haltbar – statt es im Flurfunk versickern zu lassen.

≈ 8 Min. Lesezeit

Die teuerste Frage in jedem Projekt ist nicht „Wie bauen wir das?", sondern „Warum haben wir das damals eigentlich so gebaut?". Ich erlebe sie in fast jeder Beratung. Ein neues Team übernimmt ein System, stößt auf eine merkwürdige Entscheidung – eine ungewöhnliche Datenbank, ein selbstgebautes Caching, ein Verzicht auf den naheliegenden Message-Bus – und niemand kann sagen, ob das Absicht war oder ein Unfall. Die Leute, die es wussten, sind längst im nächsten Projekt oder in der nächsten Firma. Übrig bleibt Code, der eine Entscheidung ausführt, deren Begründung verdunstet ist.

Genau dieses Problem hat Michael Nygard 2011 in seinem Blogpost „Documenting Architecture Decisions" auf den Punkt gebracht: „One of the hardest things to track during the life of a project is the motivation behind certain decisions." Das Was steht im Code. Das Warum steht nirgends – oder es steckt in einem Slack-Thread, einer Confluence-Seite, die keiner mehr findet, oder im Kopf von jemandem, der gerade Urlaub hat. Nygards Antwort darauf sind Architecture Decision Records, kurz ADRs: kleine, versionierte Textartefakte, die genau eine Entscheidung samt ihrem Kräftefeld festhalten.

Das Outcome zuerst: weniger Archäologie, schnellere Übergaben

Bevor ich über Format und Tooling rede, will ich sagen, was ADRs praktisch bringen – denn darum geht es, nicht um ein weiteres Dokumentationsritual. In den Projekten, in denen ich ADRs eingeführt habe, verkürzt sich vor allem eine Sache dramatisch: die Zeit, in der ein neues Teammitglied vom „Ich verstehe den Code, aber nicht die Logik dahinter" zum „Ich kann hier mitentscheiden" kommt. Onboarding wird von Ausgraben zu Nachlesen.

Der zweite Effekt ist subtiler, aber wertvoller. Wenn eine Entscheidung aufgeschrieben werden muss, wird sie auch bewusster getroffen. Man merkt beim Schreiben, ob man wirklich Alternativen abgewogen hat oder nur den erstbesten Weg genommen hat. Das Artefakt zwingt nicht zu mehr Bürokratie – es zwingt zu Klarheit. Und Klarheit im Moment der Entscheidung ist billiger als jede spätere Rekonstruktion.

Wichtig ist mir dabei die Abgrenzung, die Nygard selbst macht: Agile Methoden sind nicht gegen Dokumentation, sie sind gegen wertlose Dokumentation. Das 80-seitige Architekturhandbuch, das schon beim ersten Commit veraltet, ist wertlos. Ein einseitiges ADR, das neben dem Code liegt und mit ihm durch die Git-History wandert, ist es nicht. ADRs sind der Leichtgewicht-Kompromiss zwischen „gar nichts aufschreiben" und „alles totdokumentieren".

Das Format: fünf Abschnitte, eine Entscheidung

Nygard beschreibt ADRs als „a format similar to an Alexandrian pattern" – ein kurzer Text, der ein Kräftefeld beschreibt und eine Entscheidung als Antwort darauf festhält. Das Format ist bewusst minimal. Es besteht aus fünf Abschnitten: Title, Status, Context, Decision, Consequences. Mehr nicht. Und genau diese Kürze ist kein Mangel, sondern das Feature – kleine Dokumente werden eher aktuell gehalten als große.

So sieht ein ADR bei mir typischerweise aus. Es liegt als Markdown im selben Repository wie der Code, unter docs/adr/:

# 7. Use PostgreSQL as primary datastore

Date: 2021-03-12

## Status

Accepted   <!-- proposed | accepted | superseded by ADR-0012 -->

## Context

The core service needs a transactional store. Expected load is
moderate (low thousands of writes per minute at peak). The team
has deep operational experience with PostgreSQL and none with
distributed NewSQL stores. Strong consistency is a hard requirement
for the billing path.

## Decision

We will use PostgreSQL as the primary datastore for the core service.

## Consequences

Positive: strong consistency, mature tooling, no learning curve for the team.
Negative: single-primary HA needs operational care; revisit this decision
if sustained write throughput exceeds ~5k writes/second.

Der Context-Abschnitt ist der eigentliche Wert. Er beschreibt das, was Nygard „balancing of forces" nennt – die Kräfte, die auf die Entscheidung wirken: erwartete Last, Team-Skills, Konsistenzgarantien, Kosten. Wer in zwei Jahren dieses ADR liest, sieht nicht nur dass PostgreSQL gewählt wurde, sondern unter welchen Annahmen. Und – das ist entscheidend – der Consequences-Abschnitt nennt sogar die Bedingung, unter der man die Entscheidung neu bewerten sollte. Das ist kein Dogma, das ist ein Denkzustand, konserviert.

Ich achte bei der Beratung besonders auf einen Punkt: Der Context muss die Welt zum Zeitpunkt der Entscheidung beschreiben, nicht die Welt von heute. Ein ADR ist eine Momentaufnahme, kein Living Document. Das führt direkt zum wichtigsten Prinzip.

Unveränderlichkeit: ADRs werden nie überschrieben

Der häufigste Fehler, den ich sehe, wenn Teams ADRs zum ersten Mal ausprobieren: Sie behandeln ein ADR wie eine Wiki-Seite und aktualisieren es, wenn sich die Entscheidung ändert. Das zerstört den ganzen Sinn. Ein ADR ist ein Log-Eintrag, kein Zustandsobjekt.

Die Regel lautet: Ein einmal akzeptiertes ADR wird nie mehr inhaltlich verändert. Ändert sich die Entscheidung, schreibt man ein neues ADR, das das alte ablöst. Das alte bekommt den Status superseded und einen Verweis auf den Nachfolger. Die Nummerierung ist fortlaufend und wird nie wiederverwendet – 0001, 0002, 0003, auch wenn 0002 längst überholt ist. Was entsteht, ist ein Entscheidungs-Log: eine lückenlose Historie, die zeigt, welche Entscheidung wann galt und wodurch sie abgelöst wurde.

docs/adr/          # sequential, zero-padded, never renumbered
  0001-record-architecture-decisions.md
  0002-use-markdown-for-adrs.md
  0007-use-postgres-as-primary-datastore.md   # Status: superseded by 0012
  0012-switch-to-cockroachdb.md               # Status: accepted

Diesen Lebenszyklus kann man gut als Zustandsdiagramm denken. Ein ADR wandert von proposed über accepted – und irgendwann, vielleicht, zu superseded, wobei der Zeiger auf das nachfolgende ADR verweist:

stateDiagram-v2
    [*] --> proposed
    proposed --> accepted: PR merged
    proposed --> rejected: not adopted
    accepted --> superseded: replaced by newer ADR
    superseded --> [*]: points to successor ADR
    rejected --> [*]

Der Charme daran ist, dass die Git-History und die ADR-History parallel laufen. Beide leben im selben Repo, beide wandern durch dieselben Commits. Wenn ich einen alten Commit auschecke, sehe ich die ADRs, die zu diesem Stand gehörten. Das Warum reist mit dem Code durch die Zeit.

Flurfunk gegen Artefakt

Um zu zeigen, was ADRs eigentlich verändern, hilft ein einfacher Kontrast. Auf der einen Seite steht der Normalzustand vieler Teams: Entscheidungen entstehen in Meetings, in Chat-Threads, im Vorbeigehen an der Kaffeemaschine. Sie sind real, sie prägen die Architektur – aber sie sind nirgends greifbar. Auf der anderen Seite steht das versionierte Markdown-Artefakt, das im Repo liegt und über den Pull-Request reviewt wird.

flowchart LR
    subgraph Flurfunk
        A[Slack thread] --> X[lost knowledge]
        B[whiteboard photo] --> X
        C[hallway talk] --> X
        D[someone's memory] --> X
    end
    subgraph Artefakt
        E[ADR in docs/adr/] --> G[reviewed via PR]
        G --> H[versioned with the code]
        H --> I[readable in two years]
    end

Der Unterschied ist nicht, dass links keine Entscheidungen getroffen werden. Es werden dieselben Entscheidungen getroffen. Der Unterschied ist, ob die Begründung ein Artefakt wird oder verdunstet. Und das ist letztlich eine Frage der Reibung: Solange das Aufschreiben teurer ist als das Vergessen, wird vergessen. ADRs funktionieren, weil sie das Aufschreiben so billig machen – eine Markdown-Datei, ein Commit, fertig – dass es sich lohnt.

Tooling: gerade genug, um nicht im Weg zu stehen

Man braucht für ADRs kein Werkzeug. Eine Textdatei und Git reichen. Trotzdem hilft ein bisschen Konvention, damit die Nummerierung und die Vorlage konsistent bleiben. Nat Pryce hat dafür adr-tools gebaut, ein schlankes CLI, das man sich per Homebrew installiert:

# create a new, auto-numbered ADR from the standard template
$ adr new "Use PostgreSQL as primary datastore"
# -> docs/adr/0007-use-postgres-as-primary-datastore.md

# create an ADR that supersedes an earlier one
$ adr new -s 7 "Switch to CockroachDB"
# -> docs/adr/0012-switch-to-cockroachdb.md
#    and sets ADR-0007 status to "superseded by 0012"

Wer eine reichhaltigere Vorlage mag, findet mit MADR – „Markdown Any Decision Records" – ein strukturierteres Template, das explizit Platz für abgewogene Optionen lässt. Für den Einstieg empfehle ich aber fast immer die nackten fünf Nygard-Abschnitte. Man kann später verfeinern; man kann schlecht nachträglich Disziplin einführen.

Der wirkungsvollste Hebel ist ohnehin kein Tool, sondern eine Prozessgewohnheit: Eine architekturrelevante Änderung im Pull-Request wird nur gemergt, wenn das zugehörige ADR im selben PR liegt. Damit wird das ADR Teil des Reviews. Die Entscheidung wird nicht nur festgehalten, sondern im selben Moment von mindestens einer zweiten Person gegengelesen. Aus dem einsamen „Ich hab da mal was gebaut" wird ein bewusster, geteilter Schritt.

Dass das Format tragfähig ist, ist übrigens keine Einzelmeinung. Der ThoughtWorks Technology Radar stufte „Lightweight Architecture Decision Records" bereits im November 2017 auf Adopt – die höchste Empfehlungsstufe. Das ist keine Neuheit mehr, das ist etablierte Praxis.

Wo ADRs kippen – das ehrliche Kapitel

Ich wäre unehrlich, wenn ich ADRs als Selbstläufer verkaufen würde. Sie scheitern, und zwar auf drei vorhersehbare Arten.

Der erste Fehlerweg: ADRs veralten trotzdem. Wer sie nicht diszipliniert bei jeder relevanten Entscheidung schreibt, bekommt eine lückenhafte Historie. Und eine lückenhafte Historie ist gefährlicher als gar keine, weil sie Vollständigkeit vortäuscht. Wenn jemand liest „hier stehen unsere Entscheidungen" und die entscheidende fehlt, zieht er falsche Schlüsse. Er verlässt sich auf ein Log, das lügt – nicht durch Falsches, sondern durch Weglassen. Disziplin schlägt Tooling, immer. Kein CLI und kein Template ersetzt die simple Gewohnheit, im Moment der Entscheidung kurz innezuhalten und sie festzuhalten.

Der zweite Fehlerweg: zu groß, zu spät. Sobald ADRs zu Design-Dokumenten anschwellen, sinkt die Aktualisierungsquote, und Nygards ganzer Punkt – die Kürze – geht verloren. Besonders tückisch sind post-hoc geschriebene ADRs, also Begründungen, die man Wochen nach der Entscheidung nachreicht. Sie rationalisieren nur, statt das echte Kräftefeld einzufangen. Man schreibt dann nicht mehr auf, warum man sich entschieden hat, sondern warum die Entscheidung im Nachhinein gut aussieht. Das ist Selbstbetrug in Markdown – und er ist schwer zu entdecken, weil ein sauber formuliertes ADR überzeugend wirkt, ganz gleich, ob es die echten Kräfte einfängt oder nur eine hübsche Fassade davor baut.

Der dritte Fehlerweg: falsche Granularität. Ohne definierten Auslöser passiert eins von zwei Dingen. Entweder wird jede Nichtigkeit ein ADR, dann ertrinkt das Signal im Rauschen. Oder niemand weiß, was „architekturrelevant" heißt, dann wird gar nichts geschrieben. Ich definiere mit Teams deshalb früh eine grobe Faustregel: Ein ADR entsteht, wenn eine Entscheidung schwer umkehrbar ist, mehrere Teams betrifft oder eine naheliegende Alternative bewusst verwirft. Alles andere ist ein Kommentar im Code, kein ADR. Diese Schwelle darf ruhig unscharf bleiben; wichtiger als die exakte Grenze ist, dass das Team überhaupt eine gemeinsame Vorstellung davon teilt.

Ein Gedanke noch, der ADRs über reine Dokumentation hinaushebt: Wer die Bedingung im Consequences-Abschnitt ernst nimmt – „revisit if write throughput exceeds 5k/s" –, kann sie überprüfbar machen. In Building Evolutionary Architecture nennen Ford, Parsons und Kua das fitness functions: automatisierte Checks, die eine Architektureigenschaft messen. Ein ADR dokumentiert die Entscheidung, eine Fitness Function wacht darüber, dass ihre Annahme noch gilt. Beides zusammen macht aus einem toten Dokument einen lebenden Wächter.

Fazit

ADRs lösen kein technisches Problem. Sie lösen ein Gedächtnisproblem – und Gedächtnisprobleme sind auf lange Sicht teurer als technische, weil sie sich verzinsen. Jede undokumentierte Entscheidung wird mit der Zeit zu einer kleinen Schuld: Irgendwann muss jemand mit Mühe rekonstruieren, was man in fünf Minuten hätte aufschreiben können.

Der Kern ist unspektakulär und genau deshalb tragfähig: ein kurzer, unveränderlicher Text pro Entscheidung, mit Kontext, Entscheidung und Konsequenz, versioniert neben dem Code, reviewt über den Pull-Request. Kein neues System, kein Prozess-Overhead, keine Lizenzkosten. Nur die Disziplin, das Warum festzuhalten, solange es noch frisch ist.

Wenn ich einem Team nur eine Sache mitgeben dürfte, wäre es diese: Schreibt das erste ADR heute – und lasst es das ADR sein, das festhält, dass ihr ab jetzt ADRs schreibt. 0001-record-architecture-decisions.md. Alles Weitere ergibt sich, wenn die Gewohnheit einmal da ist. Und in zwei Jahren wird euch jemand dafür danken, den ihr heute noch gar nicht kennt.

Weiterführende Quellen

Kommentare