skip to content

How do you avoid over-optimising an architecture for a single quality attribute — for example designing for hyperscale, or for maximum flexibility, on day one?

level: principalimportance: should knowfreq 38%

answer

  1. Adjective → response measure + date
  2. One-way vs two-way doors sets analysis budget
  3. Design for ~10x current load, not 1000x
  4. Last responsible moment + cheap seam
  5. Fitness functions and trip-wire metrics

basics

~20 s

Tie every design choice to a measured or agreed requirement. Ask what evidence says this attribute matters at this scale, what it costs the other attributes, and whether the decision is easy to reverse later. Optimise last-responsibly, not first.

solid answer

~50 s

Over-optimising one attribute is a trade-off failure: you pay in cost, time-to-market and complexity for a benefit no requirement asked for. Guardrails: (1) demand a quantified target — 'scalable' means nothing, '10 000 writes/s by Q4, evidenced by the sales pipeline' can be checked; (2) classify the decision by reversibility — one-way doors (data model, public API and event contracts, tenancy model, promised consistency semantics) justify upfront investment, while two-way doors (framework, cache, deployment topology) should be decided fast and revisited; (3) apply the last responsible moment — defer until deferring costs more than deciding, keeping the option open cheaply via a clean seam rather than a full abstraction layer; (4) use fitness functions and trip-wire metrics so growth is detected rather than guessed; (5) budget complexity explicitly, since every component carries onboarding, debugging and on-call cost. And name the symmetric error: under-investing in a genuinely irreversible attribute is equally a trade-off failure.

go deeper

for a junior

Say that you design for the requirements you have, ask for a number rather than an adjective, and prefer simple choices you can change later.

for a middle

Add measurement (profile before optimising), designing for roughly 10x current load, and deferring reversible decisions while keeping module boundaries clean.

for a senior

Frame it as reversibility-weighted investment: one-way vs two-way doors, last responsible moment with a preserved seam, fitness functions and trip-wire metrics, and an explicit complexity budget.

for a principal

Add the organisational dimension — incentives behind over-engineering, cognitive load per team as a hard constraint, institutionalising trip-wires and ADRs, and the symmetric error of under-investing in irreversible decisions.

## The failure mode **Premature optimisation for one attribute** is choosing a design because it is excellent at *one* quality — scale, flexibility, purity, performance — without checking that the quality is required at the level you are buying, and without pricing what it costs the others. Common shapes: - **Scale theatre:** sharding, event streaming, multi-region active-active and a service mesh for a product with 200 users. Cost: months of build, a large infrastructure bill, an operational burden the team cannot carry, and slow feature delivery — which is usually the attribute that decides whether the product survives at all. - **Flexibility theatre:** plug-in points, configuration-driven everything, an abstraction over the database "in case we switch". Cost: indirection nobody can trace, and typically a leaky abstraction that would not survive a real switch anyway. - **Purity theatre:** enforcing an architectural style everywhere regardless of a component's risk or rate of change. - **Micro-performance:** hand-tuning code paths worth a fraction of a percent of latency while the p99 is dominated by an unprofiled N+1 query. Knuth's original "premature optimisation is the root of all evil" targeted exactly this — and note his clause: the 3% that matters *should* be optimised, guided by measurement. All four share one root: **a design decision made without a requirement or a measurement behind it.** ## Guardrails that actually work ### 1. Quantify the target before designing for it Replace adjectives with response measures and a date: "p99 checkout < 300 ms at 2 000 rps by Q4" rather than "fast and scalable". Then ask where the number came from. If nobody can source it, that is the finding. Design for roughly one order of magnitude beyond current load — far enough to avoid rewriting next quarter, near enough to stay cheap. ### 2. Classify by reversibility (one-way vs two-way doors) - **Hard to reverse:** data model and storage semantics, tenancy model (single vs multi-tenant), public API contracts and event schemas, security and identity model, the consistency guarantees you promise clients, choice of programming ecosystem. These deserve real upfront analysis — under-investing here is *also* a trade-off failure. - **Cheap to reverse:** cache, framework, deployment topology, queue technology, and even module boundaries inside one codebase if the modules stay clean. Decide fast, instrument, revisit. A strong answer names both directions of the error: over-engineering the reversible and under-engineering the irreversible. ### 3. Last responsible moment, with a preserved seam Defer a decision until the cost of deferring exceeds the cost of deciding. Crucially, keep the option open **cheaply**: a well-bounded module with a narrow interface preserves the ability to extract a service later without building the distributed system today. That is a seam, not an abstraction layer. ### 4. Make growth observable, not imagined Define **fitness functions** — automated checks asserting an attribute stays within bounds (a build-time dependency-direction rule, a performance test with a threshold, a coupling limit) — and **trip-wires**: "when sustained writes exceed 3 000/s, or the on-call rota crosses X, we revisit partitioning." A trip-wire converts speculation into a scheduled decision. ### 5. Budget complexity like money Every moving part has a carrying cost: onboarding time, debugging surface, upgrade toil, on-call load. Make someone state what is removed, or what will not be built, to pay for the addition. Cognitive load per team is a real constraint, not a soft one. ### 6. Watch the incentives Over-optimisation is often social, not technical: résumé-driven design, vendor pressure, an architect defending a past decision, or a team copying a hyperscaler's blog post written for a problem three orders of magnitude larger than theirs. Ask: who would build this differently if it were their own money? ## The honest counterweight "Keep it simple, optimise later" is not a universal rule — it is itself a trade-off. Late is impossible for: data models carrying years of production data, published API and event contracts with external consumers, security and privacy properties (retrofitting encryption or per-tenant isolation is brutal), and anything with regulatory implications. The discipline is not "always defer" but **match the depth of investment to reversibility × evidence**, and write down which of the two justified the choice.

  • Which decisions genuinely deserve heavy upfront investment even with no proven load?
    The irreversible ones: data model and storage semantics, tenancy and isolation model, public API and event schemas with external consumers, identity and authorisation model, and privacy or regulatory properties. Retrofitting these after years of production data or third-party integrations costs far more than the upfront analysis.
  • How do you push back when a team wants a hyperscale design for a small product without sounding obstructive?
    Do not argue style — ask for the number, its source and the date. Then price both options including operational load, and offer the cheap-seam alternative that preserves the future move plus a trip-wire metric that triggers it. You are agreeing to build it, just conditioned on evidence.
  • What is a fitness function and how does it help here?
    An automated, executable check that an architectural characteristic stays inside agreed bounds — a build-time dependency-direction rule, a latency threshold in a performance test, a coupling limit. It replaces speculative design with continuous evidence, so you learn when an attribute is actually at risk instead of guessing.

Building a house with foundations for a twenty-storey tower because you might extend one day. The foundations are a one-way door and deserve thought; the internal partition walls are not, and pouring concrete into every wall 'for flexibility' just makes the house expensive, slow to build, and impossible to change.

saying these in an interview costs you the question

  • Justifying a design with 'we might need to scale one day' and no number, source or date
  • Copying a hyperscaler's architecture for a problem orders of magnitude smaller
  • Treating 'keep it simple' as absolute, including for data models and public contracts
  • Building an abstraction layer over the database 'in case we switch' rather than keeping a clean seam
  • Optimising code paths without profiling first
  • Ignoring the operational and cognitive carrying cost of added components

context