LLD Dojo

Designing under pressure · chapter 30 of 33

Saying what you will not build

Chapter 4.2 · Part 4, Designing under pressure · about 25 minutes

What you need before this chapter: chapter 4.1, Reading a vague prompt without panicking. You already have three of the four lists an extraction produces. This chapter is about the fourth.

When you finish this chapter you will be able to:


1. A list that refuses eight real things

corpus/rate-limiter/problem.json carries an out_of_scope list with eight entries. It is the strongest example of this list anywhere in the corpus. Here is all of it:

Now read that list against what a rate limiter is actually for.

Running across several machines is not a fringe idea. It is usually the first thing anyone suggests when "rate limiter" comes up, because most real ones work that way.

Reporting on refusals is not obscure either. An operator who just got paged wants to know how often the limiter said no, and to which caller.

Every one of those eight is a feature a reasonable engineer would ask for. The list says no to all eight, in writing, before anybody gets the chance to ask.

2. The weak version, and why it fails

Picture a different list for the same problem. "No support for SOAP." "No support for XML config files." "No plugin marketplace."

Nothing there was ever going to be asked for. So refusing it costs nothing and defends nothing.

That is not scope negotiation. It reads like decision-making, and no decision was made, because nothing tempting was ever on the table.

The rate limiter's real list passes a test that one fails. Every entry names something a competent engineer would want, badly enough that leaving it out has to be a decision rather than an oversight.

Keep the two lists straight in your head. must_haves.requirements says what you are building. out_of_scope says what you looked at and chose not to build. A refusal only means something when the thing refused was worth wanting.

3. The list gets tested, on purpose

Scope negotiation stops being a writing exercise the moment somebody asks for the thing you refused.

corpus/rate-limiter/curveballs/03-denials-are-counted does exactly that. Mid-round the requirement becomes "count the denials per client key and let an operator read the number."

Read the eighth entry on that list again. Metrics and reporting on denials were ruled out from the start, in writing. This curveball asks for precisely that back.

Now take the two candidates side by side.

The one who wrote the refusal down is not caught off guard. The feature was always a live possibility, named and set aside, and now it is being granted.

The one who never wrote it down never considered reporting worth refusing. They are now retrofitting it into code that was never designed to expose it, mid-round, under the same clock.

Both end up building the same thing. Only one of them saw it coming.

4. The threshold, stated as a test with three parts

A real out_of_scope entry clears three bars at once.

Tempting. A competent engineer looking at the same prompt would plausibly want this. If nobody would ever ask, refusing it defends nothing.

Expensive. Granting it would reshape more than one line. If a feature is one call to something you already built, refusing it is not a negotiation, because there was never a cost to weigh.

Named. It traces back to the prompt, or to one of your own clarifying questions. Not to something you invented afterwards to pad the list out.

corpus/rate-limiter's twelve good_questions are where several of its eight refusals come from. Ask whether the caller needs reporting on denials, and get told it is out of scope for now. That single exchange hands you a clarifying question and an out_of_scope entry together.

5. Being caught off guard is not the same as being principled

Say the interviewer asks for something that never made your list.

Refusing it on the spot, because it feels outside what you have built so far, is not the skill this chapter teaches. It means the request surprised you, and sounding sharper next time does not fix that.

What fixes it is having seen the request coming. And that comes from reading enough real prompts to recognise the shape of the ones that keep turning up.

corpus/rate-limiter's own list maps three things people always ask about a system that says no to callers. Where does its state live? How long does that state survive? Who gets told about a denial?

Going deeper

An out_of_scope decision is not only a sentence you say out loud. It is a prediction about cost, and this corpus checks that prediction against a real number.

tools/rebudget.mjs computes every curveball's line budget from one formula: `budget = max(3, d + min(ceil(d / 2), 6)), where d is reference_diff` — the lines the reference solution actually changed to absorb the change.

Look at what happened to the three curveballs tested against this exact list.

01-sliding-window-log/budget.json records reference_diff: 1 for a third counting algorithm. The seam for a new algorithm already existed, so adding one meant a single line of registration.

02-global-backstop-cap/budget.json records reference_diff: 0. A second scope implementation slotted into a seam that was already there.

03-denials-are-counted is the one curveball granting back a refused item. It records reference_diff: 19.

Its own note says why. The base contract lists metrics as out of scope in writing, so no seam was waiting for a denial counter. Building one against a requirement ruled out on purpose would have been effort spent on a guess.

That is the trade this whole chapter is about, made numeric.

A seam you build for a feature you expect costs you nothing later and something now, in code you carry the whole round.

A feature you correctly scoped out costs you nothing now and a real, measured price later, if and when it comes back. Nineteen lines in one file, here.

The out_of_scope list does not make that cost disappear. It tells you in advance which costs you are deferring and which you are building for now. D4 of STANDARD v1.0, extensibility, is what eventually checks whether your bet paid off.

Next: chapter 4.3, Drawing a class diagram fast — where the classes your two lists named get a shared picture, before either of you writes a line of code.

← 4.1 Reading a vague prompt without panicking · All chapters · 4.3 Drawing a class diagram fast →