Record an Architecture Decision So the Next Agent Knows Why
Drafted and reviewed using AI assistance.
On this page
An architecture decision record, or ADR, explains a consequential choice: the problem, alternatives, selected approach, status, and consequences. Give the next agent the reason a boundary exists so it can assess whether that reason still applies.
A task handoff might say “the cache module is finished; add tests next.” An ADR answers a different question: “Why does this cache live here, and what would justify moving it?” That explanation should survive the conversation that produced the change.
Michael Nygard's original ADR article describes short records with context, a decision, status, and consequences. It also retains superseded decisions with a pointer to their replacements. The example below adapts that historical method; its extra fields are editorial choices, not a universal standard.
Choose a decision that has a lasting consequence
Consider a fictional service that renders public help snippets from versioned source documents. Initially it has one application process. Its proposed cache would store rendered text, never account data.
The team must decide where to place that cache. An application-local cache keeps entries in the process that handles requests. A shared cache service introduces a separate component through which several application processes can read and remove entries.
Everything about this service, its requirements, and its decisions is invented for instruction. “Accepted” in the following records describes the fictional team's decision state. It is not a real CueSpec architecture decision, a customer case, or a deployment report.
Before comparing options, define the assumptions. Otherwise an agent can justify almost any architecture by silently changing the situation. In this example, cached entries are expendable: on a cache failure, the application may render from the authoritative source. A cache key includes a document identifier and source revision. No latency target or measured improvement is claimed.
Compare the alternatives against those assumptions
| Question | Cache in the application process | Separate shared cache component |
|---|---|---|
| What additional component must be operated? | None for this example | A cache service and its client connection |
| What happens when the application process restarts? | Its in-memory entries are lost by design | Entries have a separate lifecycle defined by the chosen service |
| Who can remove an entry? | The process holding it | Callers authorized through the shared service interface |
| Does one process benefit from another's populated entries? | No, under the example's design | Yes, if both use the same service and key scheme |
| What happens when the cache is unavailable? | Render from the source | Render from the source |
| What is the initial decision pressure? | Keep the first implementation small | Avoid an additional component until there is a concrete need |
The table compares the proposed designs, not vendor performance. There is no basis here for saying that a shared service is faster, cheaper, or more reliable in a real installation. Those questions require a chosen implementation and evidence from its operating conditions.
Write a complete initial decision
The following record could live at decisions/0001-local-snippet-cache.md in the fictional repository:
# ADR 0001: Keep the public snippet cache in the application process
Status: Accepted at the initial single-process stage.
Scope: Rendered public help snippets only.
## Context
The service currently runs as one application process. Rendering uses
versioned public source documents. We want to reuse rendered text within
that process without introducing a separately operated cache component.
The source remains authoritative. Cache entries contain no account,
permission, or user-specific data. We have no requirement to share entries
between processes or centrally remove cached documents at this stage.
These are design assumptions, not measured production observations.
## Alternatives considered
A. Keep bounded entries in the application process.
B. Introduce a separate shared cache service now.
B would permit a common entry store but would also require a service
lifecycle, connection handling, and an availability policy. We do not
currently need the shared ownership it provides.
## Decision
We will use an application-local cache for rendered public snippets.
Keys will contain the document identifier and exact source revision.
The proposed cache will have a maximum of 200 entries and an entry
lifetime of 60 seconds. These are chosen example limits, not benchmark
results. The implementation must define expiry and eviction behavior.
On a cache miss or cache error, the application will render from the
authoritative source. A cache entry will not determine authorization.
## Consequences
The first implementation needs no separate cache service. A process
restart discards its entries. Future application processes would each
have their own independent entries and could repeat rendering work.
Changing a document revision changes its lookup key; old entries may
remain stored until expiry or eviction. This decision does not provide
a central purge operation, a latency guarantee, or a correctness result.
## Revisit when
We need common cache ownership across application processes, or an
explicit removal policy that the independent local caches cannot meet.
## Follow-up
Implement and check key construction, entry bounds, expiry, cache-error
fallback, and the exclusion of user-specific data. No execution or
deployment result is recorded in this example.
This record states a choice rather than listing every possible cache technology. It also describes costs the team is accepting. Repeated rendering after a restart is an expected consequence of the chosen design, rather than an unexplained defect for the next agent to “fix.”
The revisit condition matters. Without it, “keep things simple” can become an indefinite prohibition on changes that the original authors never considered.
When the conditions change, write the replacement
Now change the fictional requirements. Several application processes are planned. An editor also needs a single cache-removal operation that acknowledges removal of a document's stored entries across all application processes. This operation does not promise to recall text already delivered to a reader or cancel a render already in flight.
Those requirements expose a limitation in the first design. Independent process-local entries do not offer the proposed common removal interface. A shared component is one possible response. A coordinated invalidation system could be another, but it would add its own implementation and failure behavior.
The replacement belongs in a new record, decisions/0002-shared-snippet-cache.md:
# ADR 0002: Move public snippet entries to a shared cache component
Status: Accepted in the later fictional design stage.
Supersedes: 0001-local-snippet-cache.md
Scope: Cache storage and removal of rendered public help snippets.
## Context
The planned service has several application processes. The revised
requirement is one cache-removal interface for an editor to remove a
document's stored entries across the service and receive a result.
Independent local caches in ADR 0001 do not provide that interface.
Source documents remain authoritative, and cache data remains public.
Removing cached text is different from withdrawing the source document.
Requests must continue to check source availability before using an entry.
## Alternatives considered
A. Retain local caches and build coordination for invalidation and
acknowledgments across every application process.
B. Place the entries in one shared cache component and remove the
per-process rendered-text caches.
We choose B to place cache ownership and removal behind one interface.
We accept an additional service dependency rather than implementing
distributed coordination for independent local caches.
## Decision
We will move rendered snippet entries to a shared cache component.
Application processes will not keep a second rendered-text cache.
The key will still contain document identifier and exact source revision.
The editor-facing operation will report success only when the shared
component has acknowledged the requested removal. A failed or unknown
removal must remain failed or unknown to the caller.
Before lookup, each render will obtain the current source availability
and revision. A missing or withdrawn source must not be served from cache.
Cache lookup and write failures will fall back to rendering an available
source; they will not turn a failed removal into a success response.
## Consequences
All application processes now depend on the shared interface for cached
entries. We must define and check timeouts, entry limits, removal scope,
and the behavior of the selected service before rollout.
An in-flight render can overlap removal. The removal operation is not a
content-withdrawal guarantee; source withdrawal must be enforced by the
authoritative source check. The cache cannot recall already delivered text.
We have not chosen a vendor or measured performance. This record accepts
the design direction, not a verified implementation or a deployed service.
## Relationship to ADR 0001
The single-process assumption and absence of central removal requirements
have changed. ADR 0001 remains the explanation of the earlier design.
Its key and public-data constraints continue here; its storage choice
is replaced by this record.
## Follow-up
Select an implementation. Check common key construction, source withdrawal,
cache failure, removal failure, and overlapping render/removal behavior.
Define a rollout and rollback plan before changing the running service.
The second record makes the changed requirement visible. It also limits the guarantee: removing an entry is not the same as preventing every possible display of content. That distinction belongs in the decision because it affects what future work may safely assume.
Preserve the old record and add a clear pointer
Update the status at the top of ADR 0001 without erasing its original reasoning:
Status: Superseded by [ADR 0002](0002-shared-snippet-cache.md).
Originally accepted at the initial single-process stage.
Leave its context and consequences intact. ADR 0002 points back to ADR 0001, so a reader arriving from either direction can see the sequence. An index should identify ADR 0002 as the current cache-storage decision.
Avoid silently rewriting ADR 0001 to say that the shared design was always intended. That would remove the evidence needed to understand why the earlier implementation looked reasonable under its original assumptions.
Give an agent the decision and the question it must answer
Before proposing a cache change, read ADR 0001 and its replacement ADR 0002.
Treat ADR 0002 as the current decision in this fictional repository.
Compare your proposal with the public-data boundary, revision-aware keys,
source-withdrawal check, and removal acknowledgment requirements. Identify
which assumptions still apply and which requirement your proposal changes.
If the proposal conflicts with the current decision, draft a new ADR with
alternatives and consequences. Keep it proposed until a decision owner
accepts it. Do not rewrite the historical records to hide the change.
A useful ADR lets the next agent reason from the team's constraints. It does not make a design permanent, demonstrate that an implementation works, or grant permission to deploy a replacement.