The situation

You get a requirements document, a ticket, or a conversation that starts with “we need a service that…”. Whatever it says, it is describing a solution someone already imagined, not the constraints that make a solution correct.

Designing against stated requirements without finding the real constraints is the most common way to build something technically excellent and operationally useless.

The default

Before drawing anything, get numbers for these. Guessed numbers with an order of magnitude are fine. No numbers is not fine.

ConstraintThe question to askWhy it dominates
Load shapePeak vs average, and how spikyDetermines whether you need queueing, autoscaling, or nothing
Data volume and growthRows today, rows in 2 yearsDecides the datastore, not the query pattern
Latency budgetWhat does the caller do while waitingThe difference between sync and async is decided here
Consistency needWhat breaks if a read is 5 seconds staleUsually far weaker than people first claim
Failure toleranceWhat happens if this is down for an hourDistinguishes “add a retry” from “add a region”
TeamHow many people, what do they already runThe single most-ignored constraint
ComplianceWhere can this data live, who can see itCannot be retrofitted
MoneyWhat is the budget, per monthTurns “just use managed” into a real trade-off

The team constraint deserves emphasis. An architecture that requires expertise your team does not have and cannot hire is not an architecture, it is a plan to be rescued by consultants.

When the default is wrong

Sometimes the constraint set is genuinely unknown — a new product, no users, no load data. Do not fake precision. Instead:

  • Design for the shape you can predict, not the magnitude. “Read-heavy with bursty writes” is a design input even without numbers.
  • Pick the option with the widest range of acceptable outcomes rather than the one optimal for a guessed number.
  • Make the parts you are guessing about the cheapest to change.

This is the one situation where deliberate over-simplicity wins. The system you build before you have users exists to get you users, and its main job is to be easy to throw away.

What it costs

Constraint gathering is unpopular because it looks like delay and it surfaces disagreement. You will discover that the product manager and the compliance lead have incompatible assumptions, and that will now be your problem for a week.

That week is the cheapest it will ever be. The same disagreement discovered after launch costs a migration.

A one-hour version

If you have no time for a proper discovery process:

flowchart TD
    A["Who calls this,<br/>and what do they do<br/>while waiting?"] --> B["How much data,<br/>growing how fast?"]
    B --> C["What breaks<br/>if it is down?<br/>If it is stale?"]
    C --> D["Who operates this<br/>in 18 months?"]
    D --> E{"Any answer<br/>a guess?"}
    E -->|Yes| F["Design so that<br/>part is cheap<br/>to change"]
    E -->|No| G["Design for<br/>the numbers"]

Four questions. Most bad designs fail one of them obviously, in the first conversation, before anyone has written code.

See also

Last updated 20 Aug 2026, 00:00 UTC. history