LLD Dojo

Syllabus · F1

Requirement extraction from an ambiguous prompt

The idea

Requirement extraction from an ambiguous prompt

The prompt says a parking lot has vehicles of different sizes and rates that differ by vehicle type. That is all it says. Read only what is written, and the design already has to answer questions the prompt never raised. Does a truck take one spot or two? Does the fee round up to a whole hour, or prorate to the minute? An ambiguous prompt names the problem and leaves the conventions that decide the design unstated, and closing that gap is the whole of this skill.

The way to close it is by asking good questions, not by guessing well. Guessing well is still guessing, and a design built on a guess collapses the moment the interviewer corrects it midway through the round. corpus/parking-lot/problem.json lists twelve clarifying questions for this exact prompt, and each one earns its place by deciding something structural about the design rather than something cosmetic about its code. "How many spots does each vehicle type need, and do a truck's have to be adjacent?" decides how allocation works. "Which way does the fee round — is one minute already a full hour?" decides the shape of the timer logic. "Are the rates per vehicle or per spot occupied?" decides whether a truck ends up paying twice. None of the twelve asks about implementation. A question like "ArrayList or HashMap" would not change what the design has to do. It only changes how the thing you already decided gets built, and a question like that has no place on this list.

Skip that question about a truck's spots, and the cost does not show up right away. You design an allocator that hands out one spot per vehicle, code it, and test it, and everything passes, because your own test data never included a truck. The interviewer then asks what happens when a truck arrives, and the honest answer is that your allocator has no idea trucks might need two adjacent spots, because that was never decided. Reworking allocation midway through the round costs far more clock than asking the question would have cost at minute two.

You are done extracting once you have written down four things: the actors, the entities, the requirements, and the items you are explicitly refusing to build. The corpus format calls the whole bundle must_haves, and the fourth piece inside it, out_of_scope, is the one candidates skip. It is not a place to dump leftovers you forgot to mention elsewhere. It is an array naming real temptations you are choosing to refuse, on purpose, in front of the interviewer.

The threshold for what belongs in each list cuts both ways, and it is worth stating plainly. Nothing that lands in must_haves should be obvious from the prompt alone. If a competent engineer would take a detail for granted without asking, writing it down adds nothing. Everything in out_of_scope, on the other hand, should be something a competent engineer could reasonably have asked for. Refusing a feature nobody would ever think to want defends nothing at all.

Your own extraction has to match the reference solution's requirement list in substance, not in wording. That match is what the interviewer is actually testing: whether you listened closely to what was said, not whether you happened to guess correctly at what was meant.


Worked walkthrough

Requirement Extraction: Bridging Ambiguous to Clarified

Run it first

There is no code to run here. Read the corpus files side by side.

corpus/parking-lot/problem.json
corpus/file-system/problem.json

Open both in your editor and compare prompt_ambiguous against prompt_clarified for each.

The parking lot prompt and its extraction path

The ambiguous prompt:

Design the software for a parking lot at a shopping mall. Vehicles arrive, they park somewhere, and they pay on the way out. Not every vehicle is the same size, and the rates aren't the same for all of them either. I'd like to see it run at the end.

What it does NOT say:

The good questions that close the gaps:

"How many spots does each vehicle type need, and do a truck's have to be adjacent?"
"Is the fee per started hour or prorated to the minute?"
"Which way does the fee round — is one minute a full hour?"
"Are the rates per vehicle or per spot occupied? Does a truck pay twice?"
"What should happen when the lot is full — an exception or a null ticket?"

The clarified prompt says:

One floor, a fixed number of identical standard spots in a fixed order. A motorbike or car takes one spot, a truck takes two adjacent ones… Fees are per started hour… A full lot refuses entry with IllegalStateException.

The must-haves the clarified prompt enables:

actors: ["driver arriving", "driver leaving", "lot operator"]
entities: ["parking lot", "spot", "vehicle", "vehicle type", "ticket", "receipt"]
requirements: ["park a vehicle and issue a ticket", "truck needs two adjacent spots", "round the fee up to a whole started hour"]
out_of_scope: ["multiple floors", "payment processing", "reservations", "persistence"]

The tell: nothing here was guessable from the ambiguous prompt

The convention "fee per started hour" is the single most important design decision. Round down and a two-hour stay costs one hour's fee. Round up and it costs two. The ambiguous prompt says neither. Neither is more intuitive. One good question, asked in the first minute, decides the whole timing system.

Why out_of_scope matters

corpus/rate-limiter/problem.json lists eight items out of scope: distribution, a shared store, persistence, queueing, logging, weights per request, and a background timer. Every one of them is something you might reasonably build. That is exactly why they belong in the list. Curveball 03 then deliberately takes one back: "count the denials per client." That is how the interviewer tests whether you meant it.

The file-system extraction

Read corpus/file-system/problem.json. The ambiguous prompt says "list what's in a directory, move things around." The clarified prompt answers five questions not in the ambiguous version:

  1. Are paths absolute or relative? (Absolute only)
  2. Does mkdir build missing intermediates? (Yes, unlike write)
  3. Does write build missing intermediates? (No, unlike mkdir — this is the convention that matters)
  4. What are the four distinct failures? (Path missing, file where directory needed, directory where file needed, clashing name)
  5. How many ways can an operation fail that the caller must tell apart? (Four distinct exceptions, not one)

Question 3 is the load-bearing design decision. Answered wrong, your API is incoherent: mkdir builds parents but write does not — the inconsistency is intentional and the clarified prompt defends it.

The threshold: anchored to the reference, not to intuition

Your extraction is done when:

  1. You can name every actor who uses this system.
  2. You can list every entity the problem mentions.
  3. You can write every requirement as a sentence starting with a verb: "create a directory", "move a file", "report a denial", "charge a fee."
  4. Your out_of_scope list names real temptations, not strawmen.
  5. The reference solution's own spec matches your extraction in substance.

A requirement you left out is one the reference has. A feature you added is one the reference does not — usually, a "design decision" that the interviewer never asked for.


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.java126 lines

/**
 * Requirement Extraction: Parking Lot Example
 *
 * This main method walks through the extraction process for a parking lot system.
 * See NOTES.md for the annotated extraction path.
 */
public class Main {

  static class Extraction {
    String section;
    String[] content;

    Extraction(String section, String... content) {
      this.section = section;
      this.content = content;
    }

    void print() {
      System.out.println("\n--- " + section);
      for (String line : content) {
        System.out.println("  " + line);
      }
    }
  }

  public static void main(String[] args) {
    System.out.println("Parking Lot Requirement Extraction");
    System.out.println("=".repeat(40));

    new Extraction(
      "Ambiguous Prompt — the starting point",
      "\"Design the software for a parking lot.\"",
      "\"Not every vehicle is the same size.\"",
      "\"Rates aren't the same for all of them.\"",
      "",
      "What it does NOT say:",
      "- How many spots does a truck take?",
      "- Are spots adjacent or anywhere?",
      "- How do you round the fee?"
    ).print();

    new Extraction(
      "One Good Question — three conventions closed",
      "Q: How many spots does each vehicle type need?",
      "Q: Are they adjacent, or can they be anywhere?",
      "",
      "A: A truck takes TWO adjacent standard spots.",
      "Design impact: spotAllocator must track",
      "pairs and refuse non-adjacent assignments."
    ).print();

    new Extraction(
      "Extracted Actors",
      "- driver arriving at the entrance",
      "- driver leaving at the exit",
      "- lot operator setting the rates"
    ).print();

    new Extraction(
      "Extracted Entities",
      "- parking lot (1 floor, N spots)",
      "- parking spot (standard size)",
      "- vehicle (motorbike, car, truck)",
      "- vehicle type (open enum? closed?)",
      "- ticket (spot number + entry time)",
      "- receipt (fee + duration)",
      "- fee (per started hour or per minute?)"
    ).print();

    new Extraction(
      "Core Requirements",
      "R1: Park a vehicle and issue a ticket",
      "R2: Record which spot and when it arrived",
      "R3: Refuse entry when no suitable spot",
      "R4: A truck needs two adjacent spots",
      "R5: Free the spot when the vehicle leaves",
      "R6: Charge a fee based on stay duration",
      "R7: Charge different rates by type",
      "R8: Round fee up to whole started hour"
    ).print();

    new Extraction(
      "Out of Scope (Real Temptations)",
      "- multiple floors or multiple lots",
      "- payment processing or card handling",
      "- reservations and season passes",
      "- persistence to a database",
      "- concurrent access from several gates",
      "",
      "Why each matters:",
      "- floors: 'parking lot' could mean a garage",
      "- payment: 'they pay' could mean really pay",
      "- reservations: 'manage spots' could mean",
      "  booking them in advance"
    ).print();

    new Extraction(
      "The Load-bearing Convention",
      "Q: What happens when the lot is full?",
      "",
      "Answer A: Throw IllegalStateException",
      "  -> Lot.park(Vehicle) -> Ticket or throw",
      "  -> Type-safe, fail-fast",
      "",
      "Answer B: Return Optional<Ticket>",
      "  -> Lot.park(Vehicle) -> Optional<Ticket>",
      "  -> Type-safe, fail-soft",
      "",
      "Answer chosen by reference: Exception.",
      "API surface changed by this one question."
    ).print();

    new Extraction(
      "Threshold: Extraction is Done",
      "✓ Every actor who uses the system named",
      "✓ Every entity the prompt mentions listed",
      "✓ Every requirement as a verb",
      "✓ Out of scope names real temptations",
      "✓ Matches reference in substance, if not words"
    ).print();

    System.out.println("\n" + "=".repeat(40));
    System.out.println("The interviewer is testing whether you listen,");
    System.out.println("not whether you guess design patterns well.");
  }
}

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.

← E5 · Idempotency under retry — the claim is the answer, not the lock F2 · Explicit scope negotiation — stating what you are not building →

← all lessons