Authority lesson · 836 words
Architecture Decision Records and Evolutionary Design
Architecture is the set of expensive-to-change decisions
Architecture is not a diagram created before coding. It is the collection of boundaries, state-ownership rules, deployment assumptions, trust decisions, and operational constraints that shape future change. A framework choice may be reversible; a public API contract, tenant-isolation model, or decision to make the database the source of truth may be much harder to undo. Focus architectural attention where reversal cost and failure consequence are high.
Evolutionary design accepts that the team will learn. It does not mean postponing every decision. It means choosing a workable boundary, defining what would invalidate it, measuring the result, and keeping the next change possible. A small modular monolith with explicit ownership can be more evolutionary than premature services connected by undocumented events. The objective is not maximum flexibility; it is affordable, evidence-led change.
Use decision records as operational memory
An architecture decision record states context, decision, alternatives, consequences, status, and date. Context explains the forces that made a decision necessary: volume, compliance, staffing, latency, deployment constraints, or customer isolation. The decision must be specific enough to test. “Use microservices” is vague; “the audit worker owns crawl execution because request processes must not perform unbounded network work” is reviewable.
Consequences include benefits, new risks, migration obligations, and signals that the decision should be revisited. Keep records close to source, link them to implementation and runbooks, and supersede rather than rewrite history. A changed decision should show why the earlier one was reasonable at the time and which conditions changed. This prevents teams from repeating old arguments without remembering their original constraints.
Add fitness functions to important boundaries
A decision becomes durable when its intent can be checked. Fitness functions are tests or measurements that protect an architectural property. Tenant-scoped query tests protect isolation. Dependency rules prevent the domain layer importing infrastructure. Performance budgets protect response targets. Migration tests protect upgrade paths. A release check that request handlers never perform unrestricted crawling protects a workload boundary.
Not every property needs automation. Some require periodic threat modelling, cost analysis, disaster exercises, or manual architecture review. Name the evidence and cadence. The absence of a suitable automated test is not permission to leave a decision unverifiable. Assign an owner and define what result would trigger redesign.
Change through reversible slices
Large rewrites hide risk by combining behaviour change, data movement, interface change, and operational cutover. Prefer seams: introduce an interface, route a narrow use case through it, compare outputs, migrate data with checkpoints, and preserve rollback until evidence supports removal. Use feature flags carefully; every flag needs ownership, security review, and a retirement condition.
Review architecture after incidents and meaningful growth, not only during greenfield planning. A queue that worked at one hundred jobs may need partitioning at one million. A single region may become a continuity risk. A permissive internal API may become a public trust boundary. Evolution is a disciplined response to changed evidence, not a periodic fashion change.
Scenario
A team proposes services to fix slow releases
A web application takes two hours to deploy because tests are slow and schema changes are risky. The proposed solution is six services. Review shows the real constraints are shared tables, no migration rehearsal, and a suite that rebuilds all fixtures for every case. Services would add network and deployment complexity without removing those constraints. The better decision is module ownership, migration evidence, and test boundaries first.
- State actual forces rather than a preferred technology
- Compare alternatives against release risk and team capacity
- Define measurable fitness functions
- Record conditions that would justify later extraction
Worked example
Write an ADR for moving audits to a worker
- Context: public audits crawl multiple pages and must not block web workers.
- Decision: persist a bounded job record; a dedicated worker performs the crawl.
- Alternatives: synchronous execution, third-party queue, or scheduled task.
- Consequences: queue monitoring and retry policy become required; jobs must be idempotent.
- Fitness functions: bounded request latency, no duplicate reports, and alerts for stale jobs.
Checklist
Use this before acting
- Name the expensive-to-change decision
- Describe forces and constraints
- Record credible alternatives
- State positive and negative consequences
- Define evidence or fitness functions
- Identify migration and rollback steps
- Set a review trigger or date
- Supersede records instead of erasing history
Common mistakes
Failure patterns to avoid
- Writing ADRs for trivial implementation choices
- Recording only the chosen technology
- Using diagrams without state or trust ownership
- Treating evolutionary as make no decisions
- Keeping feature flags indefinitely
- Starting a rewrite without a reversible migration seam
Practical exercise
Create one decision with a fitness function
Select a current decision involving data ownership, integrations, background work, identity, or deployment. Write an ADR with context, alternatives, consequences, and reversal conditions. Add one executable or operational fitness function that detects violation.
Deliverable: A reviewed ADR and a linked test, metric, or recurring verification step.
Sources and further reading
Primary and official references
Topic-specific takeaway
Evolutionary architecture replaces undocumented preference with explicit decisions, evidence, and reversible change.