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 forty five minutes. The interviewer is not checking whether you have memorised 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 design | Low level design | |
|---|---|---|
| Question it answers | What does the whole system look like | How is one component built in code |
| Typical prompt | Design a link shortener for tens of millions of users | Design the classes for a vending machine |
| Main artifact | Boxes, arrows, and data flow | Class diagram and method signatures |
| Scaling talk | Replicas, sharding, caches, queues | Usually none, it is one process |
| The mistake to avoid | Hand waving the storage layer | Over 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 forty five 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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 catalogue 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:
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 worseEvery 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.
// A vending machine is the classic low level design prompt because its
// behaviour 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).
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 VendingMachinehierarchy 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.
dispensebeatsdoAction.insufficientFundsbeatserr2. 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. Recognise the idea and you have half the answer before you draw a box.
| Prompt | The idea it is really testing |
|---|---|
| Vending machine | State modeling, behavior that changes with the object's condition |
| Parking lot | Composition plus a pricing rule that can change without touching the core |
| Elevator bank | A scheduling policy kept separate from the mechanism it drives |
| Splitwise or expense sharing | Modeling who owes whom, and a clean settlement calculation |
| Library or seat booking | Access rules and the lifecycle of a borrowed or reserved item |
| Card game or deck | Invariants and shuffling without leaking the internals |
| Rate limiter | Choosing 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.
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,isSoldOutas 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 favour 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 memorise 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 twenty three 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.
Sources
- GeeksforGeeks, Difference between High Level Design and Low Level Design
- Refactoring Guru, State design pattern
- SOLID design principles, Wikipedia
- Martin Fowler, UML as Sketch
Where to take this next
- Senior system design interview prep for the high level counterpart to this round.
- Backend system design deep dive for turning a vague prompt into an architecture with data models and failure cases.
- Advanced TypeScript patterns for modeling these designs with a type system that catches illegal states at compile time.