Skip to main content

Low Level Design Interview Prep: A Method and a Worked Example

A practical guide to the low level design round, where you turn a plain requirement into clean classes under time pressure. Learn a repeatable six step method, watch a vending machine designed live as a state machine with runnable code, and see the object modeling tells interviewers actually score.

By Vikas D12 min readUpdated 24 September 2026

Software engineer with ~6 years experience, MSc Computer Science (in progress), London

Connect on LinkedIn
low level designobject oriented designcoding interview
On this page

What a low level design round actually measures

A low level design round hands you a small, familiar object from the real world, a parking lot, a vending machine, an elevator bank, and asks you to design the classes that run it in about 45 minutes. The interviewer is not checking whether you have memorized a class diagram. They are watching whether you can turn a vague requirement into a set of objects with clear responsibilities, keep the design open to the one change they will ask for near the end, and explain each decision as you go.

That last point is the whole game. Most candidates who fail this round do not fail because their code is wrong. They fail because they either freeze on where to start, or they bury the interviewer in patterns and interfaces before a single thing works. The strong candidates run a predictable process out loud, get one flow working early, and treat every design principle as a tool they reach for when the problem asks for it, not a checklist they recite.

Short answer: clarify the scope, name the core objects, give each one a single clear job, model the behavior that changes over time, then walk one real flow end to end before you polish anything. Reach for a design pattern only when a concrete requirement makes the naive version painful. Narrate the tradeoff every time you make a choice.

Low level design is not high level design

These two rounds share the words "system design" and almost nothing else. High level design is about the shape of a distributed system: which services exist, how data flows between them, where the caches and queues sit. Low level design zooms all the way in to one component and asks how its code is structured. GeeksforGeeks puts the split plainly: high level design "describes the overall architecture of a system and shows how different components interact," while low level design "includes actual logic for every system component" (GeeksforGeeks, HLD vs LLD).

High level designLow level design
Question it answersWhat does the whole system look likeHow is one component built in code
Typical promptDesign a link shortener for tens of millions of usersDesign the classes for a vending machine
Main artifactBoxes, arrows, and data flowClass diagram and method signatures
Scaling talkReplicas, sharding, caches, queuesUsually none, it is one process
The mistake to avoidHand waving the storage layerOver engineering with patterns

If you have prepared for the senior system design round and walk into a low level design round with the same playbook, you will spend your first ten minutes talking about read replicas for a problem that has no database. Notice which round you are in from the prompt. If the prompt names a concrete object and asks for classes, you are doing low level design.

A repeatable way to run the 45 minutes

The value of a method is that it removes the "where do I even start" pause. Here is a sequence that fits the time box and works across almost every classic prompt.

  1. Clarify the scope, then cut it. Ask what the object must do and, more usefully, what it does not need to do. A vending machine that only takes exact coins is a different design than one that returns change. Write down the two or three behaviors you will actually build, and say out loud which ones you are deferring.
  2. Name the core objects. Read the requirement back and pull out the nouns. Vending machine, item, slot, coin, inventory. These become your first classes. Do not invent a manager or a service yet.
  3. Give each object one job. Inventory tracks stock. A pricing rule decides cost. The machine coordinates. The moment a class starts doing two unrelated things, split it. This is the Single Responsibility Principle applied by feel, not by recitation.
  4. Model the behavior that changes over time. Many low level design objects behave differently depending on their current condition. That is a state machine, and naming the states early prevents a swamp of boolean flags later.
  5. Walk one flow end to end. Pick the happy path, a customer inserts money and buys an item, and trace it through your objects. A design that cannot complete one real interaction is not a design yet.
  6. Handle the change they will ask for. Near the end the interviewer says "now support card payments" or "now support two currencies." A good design absorbs it in one place. Show them where.

Steps three, five, and six are where the signal lives. Anyone can list nouns. Fewer people can complete a flow, and fewer still can extend it without rewriting.

The scoping move that keeps the design small

The first two minutes decide whether the rest of the round is possible. A low level design prompt usually arrives with a noun, not a specification. "Design a parking lot" could mean entry gates, spot allocation, hourly pricing, monthly passes, disabled access, electric charging, lost tickets, attendants, receipts, refunds, plate recognition, and sensors. You cannot build all of that cleanly in one interview. You need to turn the noun into a small contract before you draw objects.

Use a three-part scope readback:

Prompt ambiguityAsk thisScope cut that keeps the round fair
Payment complexity"Should I support cash, card, and refunds, or just one payment path first?"Build one payment path, leave an interface for the next
Object variants"Are vehicle types priced differently, or is every occupied spot the same for now?"Model vehicle type only if pricing needs it
Time rules"Do rates change by hour, day, or event?"Start with an hourly rule, name where event pricing would land
Concurrency"Can two users reserve the same resource at once?"State the lock or transaction boundary, then keep the code single-process unless asked
Persistence"Do you want in-memory objects or storage contracts?"Keep storage as a repository boundary, not a database design

That readback is not a delay tactic. It is how you prove product judgment. You are showing the interviewer that you can cut an object model down to the behavior that has to work today while leaving named places for the next requirement.

Worked example: designing a vending machine as a state machine

Take the most common prompt in this round. A vending machine sits in an office. It behaves differently depending on what has happened so far: before you pay it refuses to dispense, after you pay it refuses more coins until the sale finishes. That "behaves differently depending on its current condition" is the tell for the State pattern, which the pattern catalog defines as a way to "let an object alter its behavior when its internal state changes" and recommends "when you have an object that behaves differently depending on its current state" (Refactoring Guru, State pattern).

Start by naming the states, not the code. A machine is IDLE when it is waiting, HAS_MONEY once a coin is in, and briefly DISPENSING during a sale, plus SOLD_OUT per slot. The naive design scatters this across the machine:

text
class VendingMachine:
  if state == "idle" and action == "insert": ...
  if state == "idle" and action == "select": error
  if state == "has_money" and action == "select" and paid_enough: ...
  if state == "has_money" and action == "select" and not paid_enough: ...
  ... a growing pile of if-checks that every new feature makes worse

Every new behavior adds another branch, and the rules for one state end up smeared across the whole file. That is exactly the Single Responsibility violation the pattern removes. Here is a compact version that keeps each action honest about which state it is legal in. It runs, so you can trace the exact sequence an interviewer would walk you through.

js
// A vending machine is the classic low level design prompt because its
// behavior depends entirely on its current state. Each action checks the
// state it is legal in and rejects everything else in one place, instead of
// a pile of if-checks smeared across the class. Money is tracked in cents so
// there are no floating point surprises.
class VendingMachine {
  constructor(inventory, prices) {
    this.inventory = inventory; // slot code -> stock count, e.g. { A1: 1 }
    this.prices = prices;       // slot code -> price in cents, e.g. { A1: 150 }
    this.state = 'IDLE';
    this.balance = 0;           // cents inserted toward the current sale
    this.log = [];
  }

  insert(coin) {
    if (this.state !== 'IDLE' && this.state !== 'HAS_MONEY') {
      return this._reject('cannot insert right now');
    }
    this.balance += coin;
    this.state = 'HAS_MONEY';
    return this._ok(`balance ${this.balance}`);
  }

  select(code) {
    if (this.state !== 'HAS_MONEY') return this._reject('insert money first');
    if (!(code in this.inventory)) return this._reject('no such slot');
    if (this.inventory[code] <= 0) return this._reject('sold out');
    if (this.balance < this.prices[code]) {
      return this._reject(`need ${this.prices[code] - this.balance} more`);
    }
    // A legal sale: dispense one unit, compute change, reset to IDLE.
    this.state = 'DISPENSING';
    this.inventory[code] -= 1;
    const change = this.balance - this.prices[code];
    this.balance = 0;
    this.state = 'IDLE';
    return this._ok(`vend ${code}, change ${change}`);
  }

  _ok(msg) { this.log.push(`ok: ${msg}`); return { ok: true, msg }; }
  _reject(msg) { this.log.push(`reject: ${msg}`); return { ok: false, msg }; }
}

const m = new VendingMachine({ A1: 1, B2: 0 }, { A1: 150, B2: 200 });
m.select('A1'); // no money yet, rejected
m.insert(100);  // balance 100
m.select('A1'); // 100 < 150, rejected
m.insert(100);  // balance 200
m.select('B2'); // stock is 0, rejected
m.select('A1'); // 200 >= 150, vends and returns 50 change
m.log;
// [
//   'reject: insert money first',
//   'ok: balance 100',
//   'reject: need 50 more',
//   'ok: balance 200',
//   'reject: sold out',
//   'ok: vend A1, change 50'
// ]

Notice what the design bought you. Every rejection reason is specific, so a caller (or a display screen) knows exactly what went wrong. Money lives in cents, which quietly answers the follow up about floating point rounding before it is asked. And the sale logic sits in exactly one method, so the next request, "add a card reader," becomes a new way to raise the balance rather than a rewrite of select.

When you present this, do not type all of it. Sketch the states and the two or three methods, then say which parts you would flesh out with more time. Martin Fowler's guidance on diagrams applies to whiteboards too: "the essence of sketching is selectivity," you show "just those that are interesting and worth talking about" (Martin Fowler, UML as Sketch).

How to handle the follow-up without rewriting

The follow-up is not a trick. It is the round. The interviewer is checking whether the first version has clean seams where a real product would change. The vending machine version above gives you several honest answers:

Follow-up requestBad answerBetter answer
Add card paymentsPut card logic inside select beside the coin balanceCreate a payment method that authorizes an amount, then let select care only that payment is sufficient
Add refundsReset balance in several branches and hope the amounts matchCentralize a cancel or refund operation that owns balance cleanup
Add promotional pricingHard-code a discount branch in the sale methodMove price lookup behind a pricing rule that sees slot, time, and customer type
Add restocking modeLet restock mutate inventory from any stateAdd a maintenance state so sales and restocking cannot overlap
Add audit loggingSprinkle print statements in every methodEmit a sale event and a rejection event from the two result helpers

This is also where you can say no to unnecessary machinery. If the prompt has one vending machine and one currency, a distributed inventory service is noise. If it has ten thousand machines reporting stock to a fleet dashboard, that is no longer a pure low level design prompt, and you should say the storage and messaging choices belong in a separate high level design pass.

The object modeling tells interviewers score

Once the flow works, the interviewer is reading your design for a handful of habits. These are the same habits that make production code survive its second year.

  • Single responsibility, shown not named. Do not announce "I will now apply SOLID." Just keep each class doing one thing, and when asked, explain why inventory and pricing are separate. SOLID is, in the words of the reference, "five principles intended to make source code more understandable, flexible, and maintainable," and the first of them says "there should never be more than one reason for a class to change" (SOLID, Wikipedia).
  • Composition over inheritance. A PremiumVendingMachine extends VendingMachine hierarchy collapses the moment two features cross. Prefer giving the machine a pricing rule and a payment method it holds, so behaviors combine freely.
  • Program to the shape, not the concrete class. If payment is an interface with charge(amount), then coins, cards, and a phone wallet are three implementations and the machine never needs to know which it holds.
  • Name things like a teammate will read them. dispense beats doAction. insufficientFunds beats err2. Naming is the cheapest signal of seniority in the whole round.
  • Know where to stop. The strongest candidates say "I would not add a factory here, there is only one product type, it would be ceremony." Restraint reads as experience.

The point of the round is not to prove you know every pattern. It is to prove you reach for the smallest structure that makes the next change easy, and no more.

A map of common problems and the idea each one is really testing

Interviewers rotate through a small set of prompts, and each one is a costume for a specific design idea. Recognize the idea and you have half the answer before you draw a box.

PromptThe idea it is really testing
Vending machineState modeling, behavior that changes with the object's condition
Parking lotComposition plus a pricing rule that can change without touching the core
Elevator bankA scheduling policy kept separate from the mechanism it drives
Splitwise or expense sharingModeling who owes whom, and a clean settlement calculation
Library or seat bookingAccess rules and the lifecycle of a borrowed or reserved item
Card game or deckInvariants and shuffling without leaking the internals
Rate limiterChoosing an algorithm to fit a traffic shape (see the dedicated guide)

If you can name the idea, you can borrow the same method every time: clarify, name objects, assign one job each, model the changing behavior, walk a flow. For the rate limiter specifically, the algorithm choice dominates the design, which is why it gets its own worked treatment.

A quick scorecard for your own answer

Before you call the design done, score it the same way an interviewer is likely to discuss it in the debrief. This turns a vague "I think it went well" into concrete repair work.

SignalPass looks likeRepair if weak
Scope controlYou named two or three behaviors and explicitly deferred the restRestate the prompt and cut one feature before adding classes
Object ownershipEach class has one reason to changeMove pricing, inventory, scheduling, or payment out of the coordinator
State clarityIllegal actions fail in one obvious placeReplace independent booleans with named states or transitions
Completed flowOne happy path and one rejection path are traceableStop adding objects and walk a single user action end to end
Extension pointThe follow-up lands in one named object or interfaceIntroduce the smallest boundary around the volatile rule
ExplanationThe interviewer hears why each choice was madeNarrate the tradeoff, including what you are choosing not to build

The repair column matters more than the pass column during practice. If your parking-lot answer puts hourly pricing inside ParkingLot, your next drill is not another prompt. It is moving pricing into a rule object and replaying the same entry-to-exit flow until the change feels natural.

Where candidates lose the round

Failure in this round is patterned, and the patterns are avoidable.

  • The pattern astronaut. Opening with an abstract factory and three interfaces before anything runs. The interviewer wants a working object first, elegance second. Build, then refactor out loud.
  • No completed flow. A page of class stubs and no trace of a real interaction. Always finish one happy path, even a small one, before you add features.
  • Boolean soup. isPaid, isDispensing, isSoldOut as separate flags that can contradict each other. Collapse them into a single state field with named values.
  • Ignoring the extension. When the interviewer says "now two currencies," pointing at where the change lands is the highest value move in the last five minutes. A design that needs a rewrite there loses the round quietly.
  • Silence. Designing in your head and presenting a finished picture. This round is scored on reasoning, so a wrong turn you narrate and correct beats a silent correct answer.

Language and format notes

Expectations shift a little by language and by how the interview is run, and this round is asked worldwide, so plan for either format.

In Java and C++ the interviewer often expects real class definitions, interfaces, and access modifiers, and may probe thread safety if the object is shared, for example two customers at one machine. In Python and TypeScript the bar is usually more about clean structure than ceremony, so favor clear composition and typed method signatures over deep hierarchies. Whatever the language, keep money and quantities as integers, and say why.

The format also varies. A shared editor round expects code that could compile; a whiteboard or a video call round expects a class sketch plus a spoken walk through, closer to Fowler's selective sketch than a full listing. Ask which it is at the start. If you are interviewing remotely, the same remote interview setup that helps a coding round, a stable screen share and a readable editor font, matters just as much here, because your class diagram has to be legible to someone reading it upside down through a webcam.

Frequently asked questions

Is low level design only asked of senior engineers? No. It shows up from mid level upward at many product companies, and increasingly in new grad loops at firms that care about code structure. The bar scales: a junior candidate is expected to produce clean classes and one working flow, a senior candidate to also handle concurrency, extension, and tradeoffs unprompted.

Do I need to memorize all the design patterns? No. A working knowledge of a handful, State, Strategy, Factory, Observer, and Singleton (used sparingly), covers almost every prompt. Knowing when not to use one matters more than knowing all 23 by name.

Should I draw a UML class diagram? A light one helps, especially to show relationships. Keep it a sketch, not a specification. Show the classes worth discussing and their key methods, and leave the rest.

How is this different from a coding round? A coding round scores whether you can produce a correct algorithm. This round scores whether you can structure code that other people can change. You can pass a coding round with a single function; you cannot pass this one without well separated objects. If your algorithm foundations are shaky, shore them up with the coding interview patterns first.

What if I finish early? Extend your own design. Add a second currency, a maintenance mode, or a refund path, and narrate how your structure absorbs it. Volunteering an extension is a strong signal.

How much should I talk versus code? Roughly half and half. Silence reads as either stuck or hiding a wrong turn. Say what you are about to build, build it, then say what you would do next.

Should I mention SOLID principles by name? Only if the design gives you a reason. Saying "Single Responsibility Principle" after you split pricing from inventory is useful. Reciting all five principles before you have a working flow sounds rehearsed and does not help the object model.

What is the strongest final minute in this round? Summarize the design with one completed flow, one rejected illegal action, and one follow-up change. For example: "The machine accepts money, rejects a vend before payment, and card payment would plug into the payment boundary without changing inventory."

Sources

Where to take this next

Refresh role-specific skills
Recommended
Short, focused courses on the exact frameworks and tools the role uses day-to-day.
Try LinkedIn Learning

An external resource we recommend. LinkedIn Learning is not affiliated with us and we earn nothing from this link.

Continue your prep

Pair this guide with role-specific question sets and sample answer outlines.