LLD Dojo

Syllabus · F2

Explicit scope negotiation — stating what you are not building

The idea

Scope negotiation — the line you draw

An out_of_scope list is where you write down what you are refusing to build, and a weak version of that list refuses things nobody would have asked for anyway. A good one refuses things a competent engineer would actually want, which is what makes the refusal mean something. The rate-limiter's own list runs to eight entries, and none of them is a lazy omission dressed up as a decision.

Look at what it actually rules out. It rules out distribution and any shared store across processes, persistence across a restart, and eviction of idle keys once they stop being used. It also rules out per-endpoint rules, caller authentication, and any reporting on denials. A competent engineer would reasonably ask for every single one of those. That is exactly why the list exists: to say no on purpose, in front of the interviewer, rather than skip a feature by accident and hope nobody notices.

Where must_haves says what you are building, out_of_scope says what you are refusing, and a refusal only counts for something if the thing refused was tempting enough that someone would have asked for it. Refusing to build a feature nobody wanted is not scope negotiation. It is silence wearing the shape of a decision.

That distinction stops being academic the moment the interviewer asks for one of the refused features anyway. Curveball 03 is titled "Count the denials per client key and let an operator read the number," and that is precisely the metrics-and-logging entry the reference solution had explicitly forbidden. A candidate who never wrote that refusal down is now retrofitting reporting into code that was designed to ignore it entirely, on the spot, under the same clock. A candidate who did write it down loses nothing by comparison, because the retrofit was always going to be needed. The difference is only whether you saw it coming.

What an out_of_scope list is really defending is the threshold between the design in front of you and a bigger one you are choosing not to build. A rate limiter without a cluster is a client-side limiter, full stop, and that single omission decides the shape of everything downstream from it. No persistence means the limiter forgets everything on restart. No background timer means nothing refills on its own between calls. Each entry on the list changes what you are building, not only what you happen to decline adding later.

Your own out_of_scope list should name things that clear three tests at once. Each one has to be tempting enough that a real engineer would ask for it, and expensive enough that granting it would reshape the design. And each one has to be named somewhere in the problem statement or in the clarifying questions you already asked, rather than invented afterwards to look thorough.

If the interviewer asks for something that is not on your list, refusing it on the spot does not make you principled. It means the request caught you by surprise, and the fix for that is not a sharper refusal next time around. It is reading enough problems that you start to recognize what tends to get asked.


Worked walkthrough

Scope Negotiation: Saying No Deliberately

Run it first

This is a reading lesson, not a code lesson. Open these problem specs side by side:

corpus/parking-lot/problem.json
corpus/rate-limiter/problem.json
corpus/splitwise/problem.json
corpus/file-system/problem.json

In each one, find the must_haves.out_of_scope array and read it in full.

What out_of_scope actually is

Out of scope is not "things I didn't implement." It's "things I'm explicitly not building." The difference matters because the interviewer tests whether you meant it.

Parking lot

"out_of_scope": [
  "multiple floors or multiple lots",
  "payment processing or card handling",
  "reservations and season passes",
  "number-plate recognition",
  "persistence to a database",
  "concurrent access from several gates"
]

Why each one matters:

Each one changes the design top-to-bottom. If the interviewer asks "what about multiple floors," and you never said no, you're retrofitting now.

Rate limiter

"out_of_scope": [
  "distribution, a shared store, a cluster, or replication of any kind",
  "persistence, or surviving a restart",
  "eviction or expiry of idle keys",
  "weights or costs other than one unit per request",
  "queueing, throttling, or waiting on the caller's behalf",
  "per-endpoint or per-method rules, and authentication of the caller",
  "metrics, logging, or reporting on denials",
  "any background thread, timer or scheduled refill"
]

Notice "metrics, logging, or reporting on denials" is in scope as explicitly out of scope. Curveball 03 in the reference solution asks: "Count denials per client for the last hour." That's exactly the thing the reference said no to, and now the difference is whether you refactored or rewritten half your code.

The tell: what a competent engineer would ask for

An item belongs in out_of_scope when:

  1. A reasonable engineer would ask "what about X?" in the first ten minutes
  2. Answering "yes" would reshape the whole design
  3. It's mentioned in the good_questions list OR it's a known pain point in similar systems

Parking lot:

Rate limiter:

The rule: if a competent engineer would reasonably ask, it belongs in the list.

What's NOT out of scope

Edge cases don't go here. "What if the lot is full?" is a requirement, not out of scope. You handle it explicitly.

Implementation details don't go here. "Should I use an ArrayList or a HashMap?" stays in your head.

Things nobody would ask for don't go here. "Quantum computing support for parking lots" is silly.

The threshold

Your out_of_scope list is done when:

  1. Every item is something the reference solution also lists, or something from the good_questions the reference answers as "no"
  2. You can explain why each one matters to the design
  3. You can describe what the design assumes as a result — single-threaded, in-memory, one floor, etc.

The grader checks: did you name the items in problem.must_haves.out_of_scope? If you missed any, you get missed-out-of-scope and route to this lesson.

The real test: in a curveball, when the interviewer asks for something on your list, you don't panic. You say "that's out of scope as we defined it" and explain the cost of adding it.

Common misses

"Persistence" appears in every problem. It always goes out of scope unless the prompt explicitly asks for a database. You're building an in-memory model to show that you understand the domain, not a production system.

"Concurrency" — the prompt usually says "can I assume one thread?" If you ask and they say yes, write it down. If you don't ask, you have no excuse for assuming it.

"Scaling" — "millions of users" and "millions of records" are expensive and almost always out of scope in an interview. If the prompt doesn't say "this needs to scale," don't build for it.

"Error handling beyond happy path" — sounds optional, but if the requirements say "throw exceptions," error cases are IN scope and you can't avoid them.


Worked source

The 1 file of the worked design

Every file below is the one the app opens, verbatim. This is the part worth reading slowly: the prose above argues for a shape, and these are the lines that have it.

worked/Main.java112 lines

/**
 * Scope Negotiation: Stating What You Are NOT Building
 *
 * This main method walks through the out-of-scope extraction for the rate-limiter problem.
 * See NOTES.md for the detailed explanation and more examples.
 */
public class Main {

  static class ScopeItem {
    String item;
    String whyItMatters;
    String designImpact;

    ScopeItem(String item, String whyItMatters, String designImpact) {
      this.item = item;
      this.whyItMatters = whyItMatters;
      this.designImpact = designImpact;
    }

    void print() {
      System.out.println("\n" + item);
      System.out.println("  Why: " + whyItMatters);
      System.out.println("  Design: " + designImpact);
    }
  }

  public static void main(String[] args) {
    System.out.println("Rate Limiter: Out-of-Scope Negotiation");
    System.out.println("=".repeat(60));

    System.out.println("\nProblem: Implement a token-bucket rate limiter in a single process.");
    System.out.println("Questions the interviewer answered as NO:");

    new ScopeItem(
      "1. Distribution, clustering, or replication",
      "A shared rate limiter across multiple servers needs a central store.",
      "You build a single-process limiter. State lives in memory, nowhere else."
    ).print();

    new ScopeItem(
      "2. Persistence or surviving a restart",
      "A real limiter remembers tokens across crashes.",
      "Your limiter forgets on restart. It's in-memory only. No database, no file."
    ).print();

    new ScopeItem(
      "3. Eviction or expiry of idle keys",
      "A production limiter cleans up old clients automatically.",
      "Your design assumes clients either keep calling or don't matter. No cleanup thread."
    ).print();

    new ScopeItem(
      "4. Weights or costs other than one unit per request",
      "A real limiter might count large uploads as 5 units instead of 1.",
      "You charge exactly 1 token per request. No variable cost."
    ).print();

    new ScopeItem(
      "5. Queueing, throttling, or waiting on the caller's behalf",
      "Some limiters slow callers down instead of denying them.",
      "You deny immediately with an exception. No queue, no backoff."
    ).print();

    new ScopeItem(
      "6. Per-endpoint or per-method rules",
      "Real systems limit different endpoints differently.",
      "You have one limiter, one rate. Same limit for all callers."
    ).print();

    new ScopeItem(
      "7. Metrics, logging, or reporting on denials",
      "Production systems track how often and why they deny.",
      "Your limiter is silent. No counters, no logs, no metrics."
    ).print();

    new ScopeItem(
      "8. Any background thread, timer, or scheduled refill",
      "Token buckets usually refill automatically in the background.",
      "Your refill is demand-driven only. No timer thread, no scheduler."
    ).print();

    System.out.println("\n" + "=".repeat(60));
    System.out.println("The Interview Test");
    System.out.println("=".repeat(60));

    System.out.println("\nCurveball 03 (from the reference solution):");
    System.out.println("  'Count denials per client for the last hour.'");
    System.out.println("\nThis is exactly item #7: 'metrics, logging, or reporting'");
    System.out.println("you said OUT OF SCOPE.");
    System.out.println("\nTwo paths:");
    System.out.println("  Path A (you said no and meant it):");
    System.out.println("    -> Add a Metrics interface behind your limiter");
    System.out.println("    -> New file, zero edits to existing code");
    System.out.println("    -> Curveball cost: one new file");
    System.out.println("\n  Path B (you never said no, scrambling now):");
    System.out.println("    -> Retrofit logging into the deny logic");
    System.out.println("    -> Edits spread across multiple files");
    System.out.println("    -> Curveball cost: five edits, lost extensibility points");

    System.out.println("\n" + "=".repeat(60));
    System.out.println("The Threshold: Scope Negotiation is Done");
    System.out.println("=".repeat(60));
    System.out.println("\n✓ Every item is something the interviewer might ask for");
    System.out.println("✓ Every item would reshape the design if you said YES");
    System.out.println("✓ Every item is listed in the reference's out_of_scope");
    System.out.println("  (or mentioned in good_questions with a 'no' answer)");
    System.out.println("✓ You can explain why each one matters to the design");
    System.out.println("\nWhen the interviewer asks for one, you don't panic.");
    System.out.println("You say: 'That's out of scope as we defined it, and");
    System.out.println("here's what changes if we add it.'");
  }
}

The faded stage is not here, on purpose

In the app, the third stage of this lesson is a written completion: a prompt with blanks, answered in prose and then graded against what the reference extraction expects. The grading needs the app, so this page stops at the worked walkthrough.

Run the app for the drill: it is the download in the header, and it works offline once unpacked.

← F1 · Requirement extraction from an ambiguous prompt F3 · Clarifying-question quality →

← all lessons