skip to content

If architecture is "the decisions that are hard to change", what is the highest-leverage thing an architect can do about that — and what does it cost?

level: principalimportance: should knowfreq 38%

answer

  1. Fowler: eliminate irreversibility, don't just fear it
  2. Delivery capability IS architecture
  3. Seams: anti-corruption layer, ports & adapters, versioned contracts
  4. Reversal playbook: strangler fig, branch by abstraction, parallel run
  5. Buy a seam only if P(change) × cost > carrying cost

basics

~20 s

Instead of trying to predict the future perfectly, make change cheaper: automated tests, fast safe deployment, clear boundaries, and isolation layers around risky dependencies. Then fewer decisions are irreversible — but each seam you add costs complexity, so buy them selectively.

solid answer

~60 s

Fowler's own conclusion after defining architecture as "the stuff that's hard to change" is that the goal is to **eliminate irreversibility** — an architect's job is less fortune-telling and more removing the *hardness* from hard-to-change. Concretely: invest in delivery capability (test automation, continuous delivery, trunk-based development) so any change is routine; keep modules genuinely decoupled so blast radius is small; place anti-corruption layers only around genuinely volatile external dependencies; version contracts so producers and consumers can move independently; keep data models evolvable (expand/contract migrations, additive schema change); and know the incremental-replacement playbook (strangler fig, branch by abstraction) so even a "permanent" decision can be walked back a slice at a time. The cost is real and must be stated: every seam is indirection to read, test and operate; speculative abstraction ("we might swap the database") is the commonest form of waste. So you buy optionality where the probability-of-change times cost-of-change exceeds the carrying cost — and nowhere else. That calculation, not the list of techniques, is the actual senior skill.

go deeper

for a junior

Get the core idea: rather than only trying to make perfect decisions, make it easy and safe to change things later — good tests and easy deployment mean mistakes are cheap.

for a middle

Name concrete mechanisms — automated tests and CD, module boundaries, interfaces around third-party systems, backward-compatible schema changes — and acknowledge that each abstraction has a cost.

for a senior

Present the reframe explicitly (reduce irreversibility rather than predict better), cover data and contract evolvability specifically since those are the stickiest, and give the selection rule for where to buy seams versus where not to.

for a principal

Treat it as investment strategy: quantify probability × cost of change against carrying cost; argue for delivery-capability investment in business risk terms; describe the incremental-reversal playbook you'd apply to an inherited constraint; and be explicit about the domains (regulated, embedded, long-lived data) where irreversibility is irreducible and up-front rigour genuinely wins.

## The reframe Most people stop at "architecture is the stuff that's hard to change" and conclude: *therefore be very careful about those decisions*. That's half of it. Fowler's more interesting move is the follow-up: **the significant thing an architect does is to find ways to make those decisions not hard to change.** If you can make a decision reversible, it stops being architectural, and its risk evaporates. This turns architecture from a prediction problem (which humans are bad at) into an engineering problem (which we can attack systematically). So the principal-level answer to "how do you handle hard-to-change decisions?" has three parts: 1. Reduce the *number* of decisions that are irreversible. 2. For the ones that genuinely remain, decide them deliberately with recorded rationale. 3. Have a playbook for reversing them anyway when you're wrong. ## Part 1: making change cheap (the leverage) **Delivery capability is architecture work.** This is the least intuitive and most important claim. If deploying takes six weeks of coordinated release management, *every* decision is expensive to revisit — so everything is architectural, and you're forced into up-front prediction. If deploying is a ten-minute automated pipeline with instant rollback, decisions become cheap to test in production, and the irreversible set shrinks dramatically. Test automation, trunk-based development, feature flags, progressive delivery (canary, blue/green), and observability all directly reduce the cost side of the reversibility equation. The research programme behind *Accelerate* (Forsgren, Humble, Kim) found these capabilities correlate with both speed and stability — they are not a trade. **Decoupling shrinks blast radius.** A decision confined behind a well-defined boundary is reversible within that boundary. This is why modularity is the most reliably valuable architectural property: it doesn't make you right, it makes being wrong survivable. The unit that matters is *independent changeability* — can I change this without a coordinated release of anything else? **Seams where volatility is.** An **anti-corruption layer** (Domain-Driven Design) is a translation layer between your model and an external system's model, so their concepts and their churn don't leak into you. Ports-and-adapters / hexagonal architecture generalises this: the domain talks to interfaces, adapters implement them. These are the standard mechanisms for buying reversibility — but see the cost section, because putting one everywhere is a classic failure. **Evolvable contracts and data.** The stickiest decisions involve state and published interfaces, so target them specifically: - **Expand/contract (parallel change) migrations**: add the new column/field, write to both, backfill, migrate readers, then remove the old — each step independently deployable and reversible. - **Tolerant reader / additive-only schema evolution**: consumers ignore unknown fields, producers never remove or repurpose fields, so producers and consumers deploy independently. - **Contract tests** between producer and consumer, so compatibility is verified continuously rather than assumed. ## Part 2: deciding the genuinely irreversible ones well Some things resist all of this: the data you've persisted for ten years, the public API a thousand customers integrate with, the regulator you told which region the data lives in, the language ecosystem you hired for. For these: slow down deliberately (Type 1 process), consult widely (advice process), prototype the risky assumption before committing, record the decision *with its expiry conditions*, and prefer the option that preserves the most future optionality when the choices are otherwise close. ## Part 3: the reversal playbook Being able to unwind a supposedly irreversible decision is itself an architectural capability: - **Strangler fig** (Fowler): put a facade in front of the existing system, route a slice of behaviour to the new implementation, repeat, retire the old system when nothing routes to it. Turns a big-bang migration into a long series of small reversible steps, each with a rollback. - **Branch by abstraction**: introduce an abstraction over the component to be replaced, implement the replacement behind it, switch with a flag, delete the old side. Works inside a codebase without long-lived branches. - **Parallel run / dark launch**: run old and new side by side, compare outputs on live traffic, cut over only when they agree. Essential when correctness matters more than speed (billing, risk, pricing). - **Bridge and adapt**: keep the old contract alive as an adapter over the new implementation so external consumers never break, and deprecate on a published timeline. ## The costs — state these or the answer sounds naive 1. **Indirection has a permanent carrying cost.** Every abstraction layer is more code to read, more tests to write, one more hop to debug, one more place for behaviour to hide. Junior readers of the system pay this every day. 2. **Speculative generality is the dominant waste.** The database abstraction for the swap that never happened; the plugin architecture with one plugin; the configuration for the deployment topology nobody used. YAGNI applies to architecture, not just code. 3. **Optionality can degrade the primary use case.** A lowest-common-denominator abstraction over several databases prevents you using any of their good features. Portability often costs performance and expressiveness. 4. **Delivery-capability investment is real money and real time** — often several engineer-years for an organization with legacy release processes, spent on something with no visible feature output. ## The decision rule Buy reversibility where **(probability the decision changes) × (cost if it changes) > (carrying cost of the seam)**. You will never compute these precisely; the point is that stating them forces the question. Practical heuristics that fall out: - Volatile external systems (vendor APIs, partner integrations, regulatory rules) → almost always worth an anti-corruption layer. - Your own core domain → usually *not* worth abstracting; it's yours, you're going to change it constantly, and indirection just hides it. - Infrastructure you have never once changed in a decade → probably not worth the portability layer. - Anything touching persisted state or published contracts → worth investment in *evolvability* (expand/contract, tolerant readers) rather than in *swappability*. ## How this shows up in an interview The distinguishing answer names the reframe ("reduce irreversibility rather than predict better"), gives two or three concrete mechanisms, and then — crucially — volunteers the cost and the selection rule. Candidates who list only mechanisms sound like they'd abstract everything; candidates who name the trade-off sound like they've maintained a system somebody else over-abstracted.

  • Where should you deliberately NOT buy reversibility?
    In your own core domain, and anywhere the probability of change is low. Abstracting your core domain model behind interfaces 'in case it changes' is counterproductive — it changes constantly, and indirection makes each change harder to see and reason about, not easier. Likewise, portability layers over infrastructure you've never changed in a decade pay a permanent readability and performance tax for an option nobody exercises. The tell for a bad seam is that it has exactly one implementation and always will.
  • How do you justify investing in delivery capability to a business that wants features?
    Frame it as risk and option value, in their terms. Slow, risky deployment means every technical decision must be right the first time, which forces long up-front analysis and makes course-correction expensive — so the business pays for wrong bets at full price. Fast, safe deployment converts wrong bets into cheap experiments. Concretely: measure lead time, deployment frequency, change failure rate and time to restore; show what a rollback currently costs versus what it would cost; and tie it to a specific decision the business is currently afraid to make.
  • You inherit a system where an irreversible-looking decision (a shared database across eight services) is now the main constraint. What's your approach?
    Don't attempt a big-bang split. Establish which service owns each table, then introduce ownership incrementally: route all access to a table through its owning service's API (branch by abstraction inside callers), verify with a fitness function that no other service touches those tables, then physically separate the schema once the code-level dependency is already gone. Use expand/contract for any schema changes along the way, and parallel-run reads before cutting over. Each step is independently deployable and revertible; the 'irreversible' decision is unwound as a long sequence of reversible ones.

Two ways to survive a city that keeps changing: try to predict in 1950 exactly where every road will need to go, or build with utility ducts and modular blocks so re-routing later is a weekend's work. The second wins — but ducts under every square metre of pavement is also a waste. You run them under the streets that plausibly change, and pour solid concrete everywhere else.

saying these in an interview costs you the question

  • Answering only 'be careful and think hard up front' — misses the whole leverage point of making change cheap.
  • Advocating abstraction layers everywhere for hypothetical future swaps; speculative generality is the most common form of architectural waste.
  • Presenting reversibility as free — every seam has a permanent readability, testing and operational cost that must be stated.
  • Treating deployment pipelines and test automation as 'not architecture' when they directly determine which decisions are reversible.
  • Assuming a bad architectural decision requires a rewrite; strangler fig, branch by abstraction and parallel run unwind it incrementally.
  • Abstracting the core domain 'in case it changes' — that's the part that changes most and benefits least from indirection.
  • Ignoring that portability abstractions often force lowest-common-denominator usage and give up the specific capabilities you chose the technology for.

context