He had solved four hundred LeetCode problems. Asked to design a parking lot, he wrote a ParkingLot class with a park() method containing an if-else chain over vehicle types, and it worked. Then the interviewer said "add electric vehicles, they need a charging slot and a different rate", and he opened the if-else chain to add a fifth branch, and both of them looked at it, and neither said anything for a moment.
Low level design interview questions are the round that grinding algorithms does not prepare you for at all, which is exactly why Indian product companies — Flipkart, CRED, Swiggy, Razorpay, Uber, Atlassian and most others — use it as a filter. The good news is that LLD is far more learnable than DSA: it is one method applied to about a dozen recurring problems. This guide is that method, plus the problems and the traps.
What an LLD round actually tests
Not whether you know twenty-three design patterns. It tests whether the code you write can absorb a change you were not told about. Everything else — SOLID, patterns, naming — is instrumental to that.
The concrete signals interviewers grade:
- Did you clarify scope before designing?
- Are your abstractions named after behaviour rather than implementation?
- When a requirement was added, did you add a class or edit an existing one?
- Is the design testable in pieces?
- Did you talk while you worked?
The five-step method
Step 1 — Clarify, and cut scope. Two minutes, out loud. For a parking lot: do we handle payments, is it multi-floor, are there vehicle types with different rates, do we need reservations, is it a single instance or distributed? Then say what you are excluding: "I will assume single-site and skip the payment gateway integration unless you want it." Interviewers routinely score this step and candidates routinely skip it.
Step 2 — Identify the entities. The nouns and their relationships. ParkingLot, Floor, Slot, Vehicle, Ticket, RateStrategy. Write the names before any method bodies. If you cannot name it, you do not understand it yet.
Step 3 — Define interfaces for anything that varies. This is the step that decides the round. Ask yourself what is likely to change: vehicle types, pricing rules, slot allocation policy, payment methods. Each of those becomes an interface.
Step 4 — Write the core flow. One method that ties it together — park(vehicle) — and say the invariant out loud as you write it: "a slot is either free or assigned to exactly one active ticket, and this method is the only place that transitions it."
Step 5 — Extend. They will add something. A good design takes it as a new class implementing an existing interface. If you are editing a switch statement, you designed too concretely, and it is better to notice that yourself and say so than to wait to be told.
from abc import ABC, abstractmethod
class RateStrategy(ABC): # step 3: pricing varies, so it is an interface
@abstractmethod
def price(self, hours: float) -> int: ...
class HourlyRate(RateStrategy):
def __init__(self, per_hour): self.per_hour = per_hour
def price(self, hours): return int(hours * self.per_hour)
class EVRate(RateStrategy): # step 5: added mid-round, nothing else changed
def __init__(self, per_hour, charging_fee):
self.per_hour, self.charging_fee = per_hour, charging_fee
def price(self, hours): return int(hours * self.per_hour) + self.charging_fee
class ParkingLot:
def __init__(self, slots, rates):
self.slots = slots # allocation policy could itself be a strategy
self.rates = rates # vehicle type -> RateStrategy
def park(self, vehicle):
slot = self._find_free(vehicle.size) # invariant: one active ticket per slot
if slot is None:
raise LotFull()
slot.occupy(vehicle)
return Ticket(slot, vehicle, entry=now())
Then say the sentence that wins the round: "adding a vehicle type is a new RateStrategy and a registration — ParkingLot.park never changes."
SOLID, only where it earns its place
Do not recite the acronym. Use two or three principles by name where they actually apply:
- Single responsibility — the reason
ParkingLotshould not also format receipts. - Open-closed — the reason step 5 works: open for extension via a new class, closed for modification of existing ones. This is the principle LLD rounds are really testing.
- Liskov substitution — the reason a
SquareextendingRectangleis the classic counterexample, and the reason inheritance should be rarer than most candidates make it. - Interface segregation — small focused interfaces rather than one large one nobody can implement fully.
- Dependency inversion — depend on the abstraction, inject the implementation. This is also what makes the design testable, which is a separate point worth making.
Our OOPs interview questions guide covers the fundamentals underneath.
Patterns that actually appear
Five cover almost everything, and using one correctly beats naming ten:
- Strategy — anything with interchangeable rules: pricing, allocation, ranking, notification channel. The single most useful pattern in LLD rounds.
- Factory — creating the right implementation from a type, so the caller does not branch.
- Observer — notifications, event listeners, anything where one change fans out.
- Chain of responsibility — a request passing through handlers: middleware, approval flows, validation chains.
- Builder — objects with many optional fields, and a way to avoid a constructor with nine parameters.
Singleton comes up, and the honest position — that it is easy to overuse, makes testing harder and is often a global variable in a nicer coat — reads better than enthusiasm. Our design patterns interview questions guide covers each in detail.
The problems that recur
Practise these until the shape is automatic. They cover most of what gets asked:
- Parking lot — the canonical one. Slot allocation, vehicle types, pricing, tickets.
- Splitwise / expense sharing — equal, exact and percentage splits, and simplifying debts between users.
- Rate limiter — as a class library, supporting multiple algorithms: fixed window, sliding window, token bucket.
- Elevator system — the hardest common one, because it is really a scheduling problem with a state machine per lift.
- Snake and ladder / tic-tac-toe / chess — board games, testing entity modelling and turn state machines.
- Vending machine / ATM — explicit state machines, which is the point.
- Notification service — multiple channels, retries, user preferences.
- Logging framework — appenders, levels, formatters. A clean chain-of-responsibility example.
- Cache with eviction policy — LRU and LFU behind one interface.
- Online food ordering / cab booking — larger, entity-heavy, and usually scoped down heavily by the interviewer.
For each, know the three things that vary — because those are your interfaces — and the one state machine, if there is one.
State machines, done properly
An enormous share of LLD problems contain one: order status, vending machine, elevator, booking lifecycle. The weak answer scatters if status == "X" across five methods. The strong answer declares transitions explicitly.
Say it out loud: "these are the states, these are the legal transitions, and any transition not in this table raises rather than silently doing nothing." Then handle the awkward case they will ask about — what happens if a cancel arrives after shipping, what happens if two updates race.
Concurrency, when it comes up
For backend LLD rounds, expect at least a mention:
- What happens if two threads park in the last slot simultaneously.
- Where the lock goes, and how you keep it as narrow as possible.
- Why an atomic compare-and-set is better than read-then-write.
- Idempotency, so a retried request does not double-book.
You are not usually expected to write a lock-free structure. You are expected to notice the race exists, which most candidates do not.
Common failure modes
- Designing before clarifying. The single most common one.
- A god class that does everything, usually named after the problem.
- Inheritance where composition belongs. A deep hierarchy is a liability; "prefer composition over inheritance" is worth saying and meaning.
- Reciting patterns. Forcing a pattern in scores worse than not using one.
- Silence. LLD is graded on your reasoning, and if you type for twelve minutes without speaking, the interviewer has nothing to score.
- Ignoring errors. What happens when the lot is full, the payment fails, the input is invalid.
- No testability story. Mentioning how you would unit test one component in isolation is cheap and lands well.
Where each prep option actually helps
- Actually writing the code — the whole point. Design a parking lot, a rate limiter, a splitwise and an elevator as real, running code, then have someone add a requirement halfway.
- Head First Design Patterns or Refactoring Guru — enough to use five patterns correctly and know when not to.
- Existing LLD repositories on GitHub — useful for comparing your design against another, though many are over-engineered; judge them rather than copying.
- Your own codebase — find something you extended painfully and work out which abstraction was missing. That story is interview gold.
- ChatGPT — genuinely useful here: paste your design and ask it to add an awkward requirement. That is exactly the step-five drill.
- Greenroom — the spoken layer. Ari, the AI interviewer runs the design round out loud and pushes on why you chose an abstraction, which is where memorised designs stop. Honest tradeoff: Ari will not compile your classes, so write them too.
Interviewing at companies that lean on this round? The CRED backend engineer guide and Flipkart backend engineer guide both cover LLD-heavy loops, and the machine coding round guide covers the longer build-it version.
Frequently asked questions
What is a low level design (LLD) interview round?
It is a round where you design the classes, interfaces and interactions for a single system — a parking lot, a rate limiter, a splitwise clone — usually writing real code or detailed class structures within about forty-five minutes. Unlike high level system design, which covers services, databases and scale, LLD is about object-oriented modelling, and what it really tests is whether your design can absorb a requirement the interviewer adds halfway through.
How do you approach a low level design question?
Use five steps. Clarify and explicitly cut scope in the first two minutes, identify the entities and their relationships by naming the classes before any methods, define an interface for anything likely to vary such as pricing or allocation policy, write the one core method that ties it together while stating its invariant out loud, and then extend the design when the interviewer adds a requirement. If step five forces you to edit a switch statement rather than add a class, the design was too concrete.
What are the most common LLD interview questions?
Parking lot, splitwise or expense sharing, a rate limiter supporting multiple algorithms, an elevator system, board games such as tic-tac-toe or snake and ladder, a vending machine or ATM, a notification service with multiple channels, a logging framework, a cache with pluggable eviction policies, and larger prompts like food ordering or cab booking that get scoped down. For each, identify the two or three things that vary — those become your interfaces — and the state machine if there is one.
Which design patterns should you know for LLD interviews?
Five cover almost everything: strategy for interchangeable rules such as pricing or allocation, factory for creating the right implementation without the caller branching, observer for notifications and event fan-out, chain of responsibility for handler pipelines like validation or approvals, and builder for objects with many optional fields. Using one correctly scores far better than naming ten, and having an honest position on singleton being easy to overuse reads better than enthusiasm for it.
What is the difference between LLD and system design?
High level system design covers services, databases, caching, queues, sharding and scale — how a system is composed across machines. Low level design covers a single component's internals: classes, interfaces, relationships and extensibility. Many Indian product companies run both as separate rounds, and candidates who prepare only for the distributed-systems version are frequently caught out by the object-oriented one.
Why do candidates fail low level design interviews?
Most commonly by designing before clarifying scope, by writing a god class named after the problem that does everything, by using inheritance where composition belongs, by reciting patterns rather than applying one that fits, and by working in silence so the interviewer has no reasoning to score. The decisive failure is being unable to absorb the requirement added mid-round without editing existing logic, which is exactly what the round is designed to surface.