Explicit trade-offs and ADRs
Every technical decision trades one cost for another. How to judge decisions by reversibility and record the why in a short ADR.
The problem
Section titled “The problem”Six months after launch, a new developer asks why Linkr uses its own Postgres as a job queue instead of SQS or RabbitMQ. Nobody remembers. Maybe it was laziness, maybe it was a great call. Without knowing the reason, the team hesitates: changing it might break something nobody understands, and keeping it might mean carrying a mistake.
An unrecorded decision causes this problem even when it was a good one: nobody knows why it was made or when it stops applying.
What each choice costs
Section titled “What each choice costs”Some common examples, with what you get and what you give up:
| Choice | You gain | You pay |
|---|---|---|
| Managed service (RDS, Supabase) | Backups, failover and upgrades handled for you | Higher cost, less control, lock-in |
| Microservices | Independent deploys and scaling per team | Network between services, distributed consistency, operations |
| Cache in front of the database | Fast reads, less database load | Stale data, invalidation, one more piece that can fail |
| ORM | Productivity, types, migrations | Hidden queries, N+1, less optimized SQL |
| NoSQL database | Flexible schema, easy horizontal scaling | Limited joins and transactions, integrity becomes your job |
If someone says an option “has no downside”, they don’t understand the option yet. The engineering work is to make the costs explicit and pick the ones your context can afford.
The most useful question: can you undo it?
Section titled “The most useful question: can you undo it?”Classify each decision by reversibility:
- Two-way door: easy to undo. A date library, folder structure, lint tool, an email provider behind an interface. Decide quickly with enough information and move on.
- One-way door: expensive or impossible to undo. Data model, primary database, public ID format, API contracts used by third parties, main language. These are worth the time: prototype, discuss, write an ADR.
quadrantChart title How much time to spend on a decision x-axis Easy to reverse --> Hard to reverse y-axis Low impact --> High impact quadrant-1 Investigate and record quadrant-2 Decide and measure quadrant-3 Decide fast quadrant-4 Mind the details Date library: [0.15, 0.2] Email provider: [0.3, 0.45] Caching strategy: [0.35, 0.7] Public IDs: [0.8, 0.35] Data model: [0.85, 0.85] Primary database: [0.9, 0.75]
The most common mistake among people who build fast with AI is to flip this around: spend hours choosing a component library and accept whatever schema the AI generated without a second thought.
In practice: an ADR in 10 minutes
Section titled “In practice: an ADR in 10 minutes”An ADR (Architecture Decision Record) is a short file, versioned with the code, that records an important decision. Keep them in docs/adr/ with sequential numbers.
# ADR 0007: Use Postgres as the job queue
- Status: accepted- Date: 2026-03-12- Deciders: Ana, Douglas
## ContextWe need to process click events and email sends outside the request.Current volume: ~50k jobs/day, peak of ~20 jobs/s. Team of 2, with noexperience running RabbitMQ or Kafka. We already run Postgres with backups and PITR.
## DecisionUse a jobs table in Postgres with `SELECT ... FOR UPDATE SKIP LOCKED`(via the pg-boss library) instead of a dedicated broker.
## Alternatives considered- SQS: reliable, but adds IAM, another bill to track and more awkward local testing.- Redis + BullMQ: fast, but we'd have to guarantee Redis persistence.
## Consequences+ Zero new infrastructure; the job is enqueued in the same transaction as the data.+ Existing backups and monitoring already cover the queue.- The database takes on extra write load.- Not suitable for fan-out to many consumers.
## When to revisitIf we go above ~500 jobs/s sustained, or if vacuum on the jobs tablestarts affecting the latency of the main queries.What makes an ADR useful:
- Context with numbers. “Low volume” says nothing; “50k jobs/day” lets you reassess later.
- Real alternatives, with the reason each was rejected.
- Negative consequences. If it only has upsides, it’s incomplete.
- A trigger to revisit. It’s the most forgotten part and the most valuable one, because it says when the decision stops applying.
ADRs aren’t edited once accepted. If the decision changes, write a new ADR that supersedes the old one and set the old one’s status to “superseded by 0012”. That way the history of the reasoning stays readable.
Pitfalls
Section titled “Pitfalls”- Deciding by fashion. “Netflix uses it” isn’t an argument. Netflix has thousands of engineers and problems you don’t have.
- An ADR for everything. If you record every library choice, nobody reads any of them. Save them for one-way doors and decisions someone will question.
- Endless debate over a two-way door. If you can swap it in a day, decide in an hour.
- What AI tends to generate: the “internet default” choice for everything (Kubernetes, microservices, Kafka), with no regard for the size of your team or your traffic. Always ask the AI for the downsides of each suggestion and the context where it would be wrong.
Go deeper
Materials in English unless noted.
- ArticleDocumenting Architecture DecisionsMichael Nygard
The original post that proposed ADRs. Short and to the point.
- Article
- Article2015 Letter to ShareholdersJeff Bezos, Amazon
Where the distinction between one-way and two-way door decisions comes from.
- Tool