Solution strategy — five moves and what you deferred

The handful of moves that shape everything downstream, and the ones you consciously chose not to make yet. Short on purpose — a long strategy is a set of decisions nobody got around to writing.

Spine concern · arc42 §4

In one paragraph

A solution strategy is the small set of moves that shape everything downstream, each traced to the quality goal it serves and the decision that records it. Aim for five bullets. The second half is what you deliberately deferred, with the signal that says revisit — because a deferral without a trigger is indistinguishable from an oversight.


Lens 01

The Basics

Durable. What this concern is responsible for, regardless of decade or stack.

Strategy is the shape of the solution, not the plan for building it. Four or five moves, each one an answer to a ranked quality goal, each one pointing at the decision that argues for it. It is the shortest page in the spine and the one a new engineer reads first, because it explains why the system looks the way it does before they have to infer it.

A move answers a goal, or it is taste

Every bullet needs the quality goal it serves in the same line. "Event-driven ingestion" is a preference; "event-driven ingestion so a partner outage cannot fail a checkout (QG-1)" is a strategy. The difference is not phrasing — it is whether anyone can tell, later, what would make the move wrong.

Which is the test worth applying to a draft: for each bullet, name the goal that would have to change for you to reverse it. A move with no such goal is either a decision hiding in the wrong file or a technology someone wanted to use, and both are cheaper to catch here than in the third quarter of building it.

This also fixes the ordering. Because quality goals are ranked, the moves inherit the ranking, and the first bullet is the one that shaped the most. A strategy where all five bullets feel equally important is describing a system with no priorities.

Five bullets, and why length is the tell

Strategy is an index into the decisions, not a place to argue for them. The bullet says what the move is and cites an ADR; the ADR carries the options, the rejections and the consequences. Keep that split and the page stays readable for years.

So when a strategy section runs to five pages, the content is not the problem — the location is. Somebody argued a decision inside it, which means that argument is now unversioned, unciteable and invisible to anything that reads decisions by id. Move it out, leave a line behind.

The other cause of length is a strategy that has become a component list. Once every subsystem gets a paragraph, the page is competing with the structure concern and losing, because structure can be read off the code and strategy cannot.

Reversibility decides how much care to spend

Two-way doors — a library, a queue's retention setting, an internal module boundary — should be decided fast and cheaply, because being wrong costs an afternoon. One-way doors are the ones a strategy exists for: the data model, the tenancy model, the consistency guarantee you publish to consumers, the platform you buy your identity from.

Spend the argument where reversal is expensive, and say which kind each move is. A strategy that treats every choice as momentous is slow; one that treats every choice as cheap eventually ships a schema it cannot migrate. Naming the door is a one-word field that tells a reader how much scrutiny the bullet already had.

The judgement that matters is not "is this reversible" in the abstract but "reversible after how much data and how many consumers". Almost everything is a two-way door in week one. Record the point at which it stops being one.

Deferring is a strategic act, if you write the trigger

Multi-region, a second database engine, a permissions model richer than roles, an internal platform. The right answer is often not yet, and not yet is a legitimate strategy entry — as long as it comes with the signal that would change it.

The trigger is what separates a deferral from an oversight. "Not multi-region yet" reads as ignorance to anyone who arrives later; "not multi-region until a contracted customer requires an in-region RPO, which C-3 says is 2027 at the earliest" reads as a decision, and it tells the next person exactly what to watch.

Write what the deferral costs you too, because deferrals are not free — they are usually a known ceiling. A single region caps availability; roles-only permissions cap which deals you can close. A ceiling nobody wrote down is a ceiling somebody promises past.

A strategy bullet is complete when

It names the goal
The ranked quality goal the move serves, by id. No goal means the move cannot be argued with or retired.
It states the move in one sentence
If it needs a paragraph, it is a decision. Write the ADR and cite it from here.
It cites the decision
One ADR id. The bullet is the index entry; the record holds the alternatives and the consequences.
It says which door it is
One-way or two-way, and after how much data or how many consumers it stops being reversible.
It names what it makes harder
Every move trades something away. The trade-off is the part a future reader needs and the part drafts omit.

In the spec

What the concern looks like once it is written down. No machine layer here — this one is derived from the decisions and quality goals it cites, so the artefact is the page a person reads.

arch/06-strategy.md — five moves, each with its goal and its decision
# 6. Solution strategy

| Quality goal | The move | Door | Recorded in |
|---|---|---|---|
| QG-1 checkout completes in 2s p95 | Modular monolith, boundaries enforced by an import test. No services until a team is measurably blocked. | two-way | ADR-0003 |
| QG-1 | Payments called through one adapter; a provider outage fails the order, never silently succeeds. | one-way once tokens are stored | ADR-0011 |
| QG-2 tenant data never crosses regions | Single region, tenant id on every row, residency asserted by a query test. | one-way after first EU tenant | ADR-0007 |
| QG-3 a release is reversible in 10 min | One deployable unit, expand-migrate-contract schema changes, flags for anything user-visible. | two-way | ADR-0014 |
| QG-4 cost per tenant under $4/mo | Buy identity, email and search. Build only the pricing engine — it is the product. | two-way per vendor | ADR-0019 |

## Deliberately deferred

| Deferred | Costs us | Trigger to revisit | Owner |
|---|---|---|---|
| Multi-region | Availability capped ~99.9; no in-region RPO | A signed contract requiring in-region recovery (C-3: not before 2027) | arch |
| Per-field permissions | Cannot close enterprise deals needing it | Second lost deal citing it | product |
| Read replicas | Reporting contends with checkout | Reporting queries exceed 15% of DB time | arch |
| Own feature-flag service | Vendor is a checkout dependency | Vendor availability under 99.9 for two quarters | arch |
The same bullet, before and after
# Before — four nouns and no argument

- Event-driven architecture
- Kubernetes
- GraphQL federation
- CQRS for reporting

# After — each move traced, and one of them deleted

- Queue for order fulfilment (QG-1): a partner outage must not
  fail a checkout. Two-way door. ADR-0009.
- Managed container platform, one cluster (QG-3): rollback in
  minutes without bespoke tooling. ADR-0014.
- One REST API, versioned (QG-1): federation solves a problem
  we do not have — four consumers, one team. ADR-0021 rejects it.
- Reporting reads a nightly snapshot, not the write model
  (QG-3): keeps reporting load off checkout. Revisit when the
  snapshot lag breaches 24h. ADR-0022.

Deleted: CQRS. It appeared because the write model is awkward,
which is ADR-0022's problem, not a strategy move.

Lens 02

The Current

Reviewed 2026-09-09

How it actually plays out in production now. Dated, because this is the part that decays.

The boring answer is usually right now, and the defaults have moved: a modular monolith on a managed relational database in one cloud region carries a startup a long way, and every deviation from it should name the goal that justifies the deviation. Most of what a current strategy actually decides is what you are not going to build at all.

The default shape, and what earns a deviation

Start from one deployable unit with enforced internal boundaries, one managed relational store, one region, and a queue for the work that must not happen in the request. That shape is well-understood, cheap to operate, and easy to split later along boundaries you have already enforced in code.

Splitting into services is an answer to a specific goal — independent deploy cadence for a team that is genuinely blocked, or a workload whose scaling profile is nothing like the rest. Both are real. Neither is the same as wanting the diagram. If a strategy proposes services and cannot name which goal the split serves, the honest bullet is a modular monolith with the boundary enforced by a build check.

The same discipline applies to every fashionable move: event sourcing, a second language on the backend, a service mesh, a bespoke internal platform. Each is right sometimes. Each costs an operational surface a small team pays for daily, and that cost belongs in the bullet next to the benefit.

Most of the strategy is now a make-or-buy list

Identity, payments, email, search, feature flags, observability, inference. A current system buys most of its undifferentiated capability, so the strategy's real content is which capabilities you build because they are the product and which you rent because they are not.

Write each purchase as a strategy move, not a procurement footnote, because each one is an availability ceiling and a per-unit cost you do not control — the arithmetic the context concern makes explicit. Buying is usually correct and rarely free.

The line to hold is around the thing that differentiates you. Renting your identity provider is sound; renting the model of your own domain is how you end up with a product shaped by someone else's data model and a migration nobody will fund.

Cost per unit is a strategy input

Cost per tenant, per request, per active user — not the monthly bill. The bill tells you what happened; the unit figure tells you whether the shape you chose survives ten times the volume, which is the question the strategy is answering.

Two moves that meet the same functional need can differ by an order of magnitude at scale, and the difference is usually invisible at current volume. That is precisely when the strategy is written, so put the projected unit cost in the bullet rather than discovering it in a finance review eighteen months later.

Commitments and reserved capacity are dated facts, so they belong in constraints with an expiry, not in strategy. Strategy names the shape; constraints record the contract that made one shape cheaper for the next two years.

A strategy for a system that already exists

Greenfield strategies are the exception. Most of the time the moves apply to something already in production with users on it, which means every bullet needs the path as well as the destination: what runs in parallel, how the data moves, what the rollback is, and how you know the new path is correct before the old one is deleted.

Strangler-fig migration, dual writes with reconciliation, shadow reads compared for a fortnight. These are the mechanics, and a strategy that names an end state without one of them is a wish. The migration is also where the cost lands: running both paths for a quarter is the real price of the move, and it is the number that gets left out.


Lens 03

Future-ready

What changes when agents write and operate the code. Opinionated on purpose.

When generating code is cheap, the constraint moves to what humans can review and what production can absorb. Strategy is where that shift shows up first: the moves worth making are the ones that shrink blast radius, keep changes verifiable and keep the number of ways to do a thing small — because volume of code is no longer the thing in short supply.

Cheap code changes which moves are worth making

Two of the classic reasons to avoid a move were the cost of writing it and the cost of the boilerplate around it. Both have collapsed. What has not collapsed is the cost of understanding it at 3am, reviewing a change to it, migrating it, or explaining its failure to a customer.

So the strategy's centre of gravity shifts toward reversibility, isolation and verifiability. A move that halves the code and doubles the blast radius is now a worse trade than it looked three years ago; a move that adds code but keeps a failure inside one boundary is a better one. Optimise for the expensive part, and the expensive part is no longer typing.

This is also the honest answer to premature abstraction. It has always been tempting and it is now cheap to produce and just as expensive to own — an agent will build the generalised version enthusiastically, and the ownership cost falls on whoever is on call.

Uniformity is now a strategy-level move

One HTTP client, one error shape, one persistence pattern, one way to schedule background work. That used to be a style guide; when a large share of code is generated it becomes architecture, because generation multiplies whatever variety it finds and each variant is a separate thing to operate.

Say it in the strategy and enforce it with a guardrail. "One way to do a thing, and the way lives at this path" is a bullet an import-boundary test or a lint rule can defend, which is the difference between a stated preference and a property of the system.

The cost of skipping it is not ugly code. It is four retry implementations with three different backoff behaviours, discovered during an incident caused by the one that had none.

The one-pager an agent actually reads

Strategy has no spine.yaml key, deliberately — it is derived from decisions and quality goals, and inventing a machine layer for a derived view would be two sources of truth for one fact. What it has instead is brevity, and brevity is what survives a context window.

That matters because an agent with no statement of shape does not stop and ask. It pattern-matches to the most common architecture on the internet, which is a plausible average of a million tutorials and is not your system. Five bullets naming the shape, the defaults and the deviations is the cheapest correction available, and it fits in every session rather than being fetched when somebody remembers.

An agent is also good at the review that keeps this page honest: finding moves with no cited decision, decisions that contradict a bullet, and deferrals whose trigger has quietly been met.

A deferral without a trigger will get built

The economics of "we'll add it later" have changed. Later is now cheap enough that an agent, asked for a small feature, will happily add the multi-tenant abstraction, the plugin system or the second cache you had decided not to have yet — and the diff will look competent.

So the deferral list stops being documentation and becomes a constraint on generation: these are the things we chose not to have, and here is the signal that would change that. Written that way, it is checkable in review; written as an omission, it is an invitation.

Give each deferral an owner and a trigger you can actually observe — a contract term, a load figure, a support-ticket pattern. A trigger phrased as "when it becomes a problem" is not a trigger; it is the same silence with better manners.


How this concern fails

Named, because a failure mode you can name is one you can spot in your own repository before it costs you a quarter.

The technology list

Five nouns — Kafka, Kubernetes, GraphQL, CQRS — with no goal beside any of them. Nobody can tell which are load-bearing, so nobody can retire one, and the list reads as a shopping receipt rather than an argument.

The five-page strategy

Decisions argued in the wrong file. The reasoning is now unversioned and uncitable, the page is too long for anyone to read, and the ADR that should hold the argument is empty or missing.

Strategy as roadmap

Quarters, features and dates instead of shape. It ages out in six weeks and it answers a different question — what we will build next, not why the system looks like this.

The unwritten deferral

Everyone assumes multi-region is coming. Nobody wrote the trigger or the ceiling, so somebody eventually promises a recovery objective the architecture cannot meet.

The default nobody argued with

Services because that is what the last place had. No goal, no ADR, and the operational cost lands at team size six, by which time the boundaries are wrong and expensive to move.

The move with no trade-off

Every bullet lists benefits only. A future reader cannot tell whether today's pain was known and accepted or entirely unforeseen — and those two situations call for opposite responses.


Go deeper


Frequently asked

What is a solution strategy in software architecture?

It is the small set of moves that shape everything downstream: the fundamental technology and structural choices, each traced to the quality goal it serves. It is not a plan, a roadmap or a component list. Four or five bullets, each naming the move, the goal behind it, whether it is reversible, and the decision record that carries the argument.

How long should a solution strategy be?

One page, five bullets, plus the deferral list. Length is a diagnostic rather than a style question: a long strategy almost always means a decision was argued inside it instead of being written as a decision record, or the page has drifted into describing components. Move the argument to an ADR and leave a one-line index entry behind.

What is the difference between solution strategy and architecture decisions?

Decisions are the record — one per consequential choice, with the options rejected and the consequences accepted. Strategy is the index: the four or five decisions that shaped the most, in one place, in the order they matter. If you can only keep one, keep the decisions; strategy is derived from them and is valuable because it is short, not because it is separate.

How does this map to arc42 section 4?

It is arc42 section 4 with two additions. arc42 asks for the fundamental technology and decomposition decisions and for the reasoning to be brief, which is exactly right. The Spine requires each move to cite the quality goal it serves and the ADR that records it, and adds an explicit deferral list with a trigger per entry — the half of a strategy arc42 leaves to convention and most teams therefore leave out.

Why does strategy have no spine.yaml key?

Because it is a derived view. Every fact in it already exists as a ranked quality goal or an accepted decision with an id, and giving a derived view its own machine layer would create a second copy to keep in sync. That is the decision-first thesis stated honestly: the concerns with a machine layer are the ones something acts on directly, and strategy is read by people.

How do you record something you decided not to build yet?

As a row with four fields: what is deferred, what the deferral costs you today, the observable signal that would change the answer, and who is watching for it. The signal is the part that does the work — a contract term, a load threshold, a repeated support pattern. "When it becomes a problem" is not a trigger, and a deferral without one is indistinguishable from something nobody thought about.

Should an AI coding agent draft the solution strategy?

It can draft candidate moves and it is genuinely useful for the maintenance pass — flagging bullets with no cited decision, decisions that contradict the stated shape, and deferrals whose trigger has been met. The ranking is not delegable, though, and neither are the deferrals: both encode what the business is willing to be bad at, which is not inferable from the repository.


The other eleven concerns

Pages are being written one at a time. The ones without a page yet are still in the template, with prompts instead of prose.

Solution strategy

Take the template, not the idea

The zip, the single-file variant, the schema and the agent bundle. Nothing behind an email address.

Free to fork, modify and use commercially. No attribution required.