A stakeholder tells you the system must be scalable. How do you turn that into a requirement you can design and test against?
Rewrite it as a quality-attribute scenario: a named stimulus arriving at a stated rate, hitting a stated system state, with a response measured at a percentile over a defined window. A number with a load figure, a duration and a named sacrifice is designable and testable; a list of maxima is not.
What the interviewer is scoring
- Does the candidate refuse the word "scalable" and ask what is growing, rather than assuming throughput
- Whether the target they produce carries a load figure, a measurement window and a stated system state, not just a latency number
- That they treat quality attributes as competing rather than additive, and say out loud which one they are willing to spend
- Whether they can describe the harness that would fail the build, including how an open-model load generator differs from a closed one
- Does the candidate write a degraded-mode clause, so the requirement still means something during a deploy or an AZ loss
Answer
"Scalable" names a direction, not a requirement
The first move is to ask what is growing. Scalability is a rate of change, so the sentence is incomplete until someone says which quantity increases and along what axis: concurrent users, requests per second, tenants, dataset size, events per partition, number of integrations. These have almost nothing in common architecturally. A system that must go from ten thousand to a million rows needs indexing and partitioning strategy; a system that must go from five hundred to fifty thousand concurrent connections needs a different I/O model; a system that must go from twelve tenants to twelve hundred needs isolation and noisy-neighbour control. Committing to a design before the axis is named means you optimise the wrong dimension and find out in production.
The second move is to ask who suffers if it is missed and what they do instead, which converts a preference into a business consequence you can rank against everything else the same stakeholder wants.
The six-part scenario
The durable format for this is the quality-attribute scenario from ATAM and the SEI's architecture work, which forces six slots: a source of a stimulus, the stimulus itself, the artefact it arrives at, the environment the system is in when it arrives, the response, and the response measure. Four of those slots carry most of the weight in an interview.
The stimulus must be a concrete event, not a state of affairs. "High load" is not a stimulus; "a burst of checkout submissions at three times the daily peak" is. The environment is the slot candidates skip and the one that decides whether the requirement survives contact with reality, because it states what else is true: cold caches, one availability zone unavailable, a rolling deployment in progress, the nightly reconciliation job running. A latency target that only holds when everything is healthy is a target that is never in force during the incidents you care about. The response says what the system does, including the option of shedding load or degrading, and the response measure is the number.
Percentile, load figure, duration: why all three
A response measure needs a percentile, an offered load, and a measurement window, and it is close to meaningless if any one is missing.
Without a percentile you get an average, and averages hide exactly the population that churns. Averages are also not composable across a request that fans out. Take a page that makes twenty backend calls and assume, generously, that their latencies are independent: the probability that all twenty land inside their own p99 is 0.99 to the twentieth power, roughly 0.82. So about one page view in five contains at least one p99-tail call. That arithmetic is why a per-hop p99 does not give you a p99 page, and why staff-level answers pick the percentile from the fan-out, not from habit.
Without a load figure the percentile is unfalsifiable, because p99 at 40 requests per second and p99 at 4,000 are different claims about different systems, and only one of them costs money. The load figure is also what makes the requirement a design input: it tells you the concurrency you must hold, which tells you connection pool sizes, thread models and partition counts.
Without a duration you cannot compute the percentile at all, since a percentile is a property of a sample, and you cannot distinguish a system that survives a sixty-second burst from one that holds steady state for an hour. Duration is where the interesting failures live: connection pool exhaustion, queue growth, garbage-collection pressure, disk filling with WAL, autoscaler lag. Short tests report the behaviour of a system that has not yet had time to break.
A list of maxima is not a specification
Quality attributes trade against each other, so a document that asks for the best possible value of each one has specified nothing. Synchronous cross-region replication buys durability and costs write latency. Strong consistency during a partition costs availability. Validating a token and decrypting a field per request costs milliseconds. The indirection that makes a system modifiable costs call depth. Every one of these is a real exchange, and cost is the currency all of them are ultimately paid in.
The consequence is procedural: if the trade-off is not made explicitly by the people accountable for the outcome, it gets made implicitly by whoever writes the code under deadline, and nobody will know it happened until an incident. So the deliverable is not a list, it is a ranked set. The utility-tree technique is to collect candidate scenarios, then have the business rank each for importance and the architects rank each for implementation difficulty, and treat the high-importance, high-difficulty scenarios as the architecturally significant ones. Everything ranked low is thereby documented as a thing you are willing to spend. Being able to say "we will accept a two-second worst case on the reporting export in order to hold 200 milliseconds on checkout" is the sentence that tells an interviewer you have done this before.
Before and after
Take the vague requirement and put it through the slots.
Before: "The checkout service must be scalable and fast."
After:
| Slot | Value |
|---|---|
| Source | Authenticated customers on the web and mobile clients |
| Stimulus | Checkout submissions at 1,200 per second, being 3x the observed daily peak |
| Artefact | The checkout API, measured at the edge load balancer |
| Environment | Steady state for 30 minutes, with one of three availability zones drained and a rolling deploy in progress |
| Response | Orders are accepted and persisted, or rejected with a retryable 503 |
| Response measure | p99 end-to-end latency under 400 ms, error rate under 0.1%, and no more than 0.5% of requests shed |
Every clause in the "after" row is a design constraint you can act on and a number you can measure. The zone-drained clause sets your headroom requirement at roughly 1.5x per-zone capacity. The rolling-deploy clause forces graceful shutdown and connection draining to be real rather than assumed. The shedding allowance is what makes the target achievable instead of aspirational, because it tells the design that overload is a first-class case with a defined behaviour.
The scenario becomes the acceptance test
Once written in this form, the scenario turns into three artefacts almost mechanically. It becomes a load test whose assertions are the response measures, run on a schedule and capable of failing a pipeline. It becomes a production SLO with an error budget, measured on the same signal at the same observation point, so the test and the monitor cannot disagree about what "p99 latency" meant. And it becomes a line in the architecture decision record explaining why the design has the shape it has.
// k6: the scenario's numbers ARE the pass/fail criteria, not a report footer.
export const options = {
scenarios: {
checkout_peak: {
// Open model: offered load stays at 1,200/s even if the system slows.
// A closed model (fixed VUs looping) would quietly reduce the load
// as latency rises, and report a pass for a system that fell over.
executor: 'constant-arrival-rate',
rate: 1200, timeUnit: '1s',
duration: '30m',
preAllocatedVUs: 800,
},
},
thresholds: {
'http_req_duration': ['p(99)<400'],
'http_req_failed': ['rate<0.001'],
},
};
That open-versus-closed distinction is where otherwise good answers fall over. A generator with a fixed pool of virtual users waiting for each response to come back before sending the next one throttles itself in lockstep with the system under test. As the service degrades, the offered load drops, the queue never grows, and the recorded percentile improves. The harness has measured the system's capacity to slow down, and your requirement has been verified against a load your users will never politely reduce.
A quality-attribute requirement is testable only when someone could, in principle, demonstrate that you failed it. Percentile, load, window, system state, and the attribute you agreed to sacrifice — remove any one of them and you are back to "it must be scalable" with more syllables.
Likely follow-ups
- The business says every attribute is critical and refuses to rank them. What do you do next?
- How would you set the error budget for this target, and what happens operationally when it is exhausted?
- Your load test passes at p99 but real users complain. What are the likely explanations?
- How do you write a testable scenario for modifiability or for security, where there is no obvious percentile?
Related questions
- The business says the system must be highly available. How do you turn that into a number, and what does that number cost?mediumAlso on slo and quality-attributes4 min
- The business insists on a requirement that nobody can test — "the system must be intuitive". What do you write instead?mediumAlso on non-functional-requirements4 min
- What goes in a BRD versus an FRD versus a user story, and how do you decide which to produce?mediumAlso on non-functional-requirements5 min
- How do you handle cache invalidation, and what goes wrong at scale?hardAlso on scalability3 min
- How would you set an SLO for a service, and what is an error budget for?mediumAlso on slo5 min
- What problem do virtual threads actually solve, and when do they not help?mediumAlso on scalability5 min
- The operations team says there is no rule for this, they just use judgement. How do you model that?hardSame kind of round: design6 min
- You have been handed a scope and a budget, and the budget cannot buy the scope. What do you put in front of the customer?hardSame kind of round: design5 min