pa-10 step 01 — The ADR (Architecture Decision Record) template
An ADR captures one architecturally-significant decision: the context, what you chose, what you rejected, and the consequences. Keep it short, immutable (supersede rather than edit), and in the repo next to the code. The value is the preserved why — so the decision isn't re-litigated or accidentally reverted six months later.
Rule of thumb: write an ADR when a decision is costly to reverse, affects multiple teams/services, or someone will ask "why did we do it this way?" Don't ADR trivial, easily-reversible choices.
Template
# ADR-0007: Use the transactional outbox for service-to-broker events
- Status: Accepted # proposed | accepted | superseded by ADR-00NN | deprecated
- Date: 2026-06-14
- Deciders: platform-arch, payments-team, eng-leads
- Supersedes: —
- Related: ADR-0003 (eventing backbone = Kafka), pa-05, pa-03
## Context
What forces are at play? State the problem and the constraints — the
"-ilities" in tension, the scale, the existing decisions this builds on.
> Services must update their DB and publish a domain event. A best-effort
> dual write loses events on a crash (the dual-write problem), causing
> downstream inconsistency we've already been paged for. We need
> at-least-once publication tied to the DB commit, across ~40 services, in
> Go and Java, on our existing Postgres + Kafka.
## Decision
The choice, stated plainly.
> Adopt the **transactional outbox**: services write the event into an
> `outbox` table in the same DB transaction as the state change; a relay
> (CDC via Debezium where available, polling elsewhere) publishes to Kafka
> and marks rows sent. Consumers MUST be idempotent (dedup by event id).
## Alternatives considered
List the real options and *why each was rejected* — this is the part that
prevents re-litigation.
> - **Best-effort dual write** — rejected: loses events on crash (the
> problem).
> - **Distributed 2PC (DB + Kafka)** — rejected: blocks on coordinator
> failure, poor availability, operationally heavy.
> - **Event sourcing (log is source of truth)** — rejected for now: large
> migration + team-readiness cost; revisit per-service.
## Consequences
What becomes easier, what becomes harder, and what you'll revisit.
> + No lost events; state and events never diverge.
> + Works on existing Postgres + Kafka; incremental per-service adoption.
> − At-least-once → every consumer needs idempotency (provide a shared lib).
> − A relay/CDC component to operate (monitoring, lag alerts).
> Revisit if: CDC lag becomes a problem, or a service needs event sourcing.
Tasks
- Write an ADR for a decision in your system using this template.
- Find an old decision whose reasoning is now lost — write its ADR retroactively and notice how much context was nearly gone.
- Practice the "Alternatives considered" section: the senior signal is what you rejected and why, not just what you chose.