All posts

Low-Level Design Interview: How to Approach an LLD Round (With a Worked Example)

Low-Level Design Interview: How to Approach an LLD Round (With a Worked Example)

The interviewer says, "Design a parking lot," and goes quiet. You start typing class ParkingLot within ten seconds, add a list of spots, then a Car, then realise you have not asked whether motorbikes exist, whether there are multiple floors, or how people pay. Twenty minutes later you have three hundred lines of code, one class doing everything, and the interviewer asks, "Now how would you add electric vehicle charging?"

A low-level design round is not a typing test. It is a test of whether you can turn a vague problem into a set of classes that each do one job, talk to each other through clean interfaces, and survive the next requirement without being rewritten.

The short answer

A low level design interview asks you to model a small, concrete system as objects: the classes, their fields and methods, the interfaces between them, and how they collaborate in the main flows. It differs from high-level system design, which is about services, scale, storage and traffic, and from a machine coding round, where you must ship working code in a fixed time. The reliable approach is: clarify requirements and scope, list the entities, give each one responsibilities, draw the relationships, define interfaces, walk through the key flows, add design patterns only where they solve a real problem, then talk through extensibility, edge cases and concurrency. Interviewers mark you on structure and reasoning far more than on syntax.

What an LLD round is, and what it is not

The same problem, "design a parking lot", can show up in three different rounds, and each wants something different.

  • High-level system design asks how the system runs at scale: which services exist, where data is stored, how requests are routed, what happens when traffic grows tenfold or a database fails. You draw boxes for services, caches and queues. Classes barely appear. If that is the round you are facing, read how to ace your system design interview.
  • Low-level design, sometimes called an object oriented design interview, zooms into one component. You decide the classes, what each is responsible for, which interfaces separate them and how they interact. The question is not "can this handle a million users?" but "can this code absorb a new requirement next month without a rewrite?"
  • A machine coding round gives you 60 to 120 minutes to build a working program, usually runnable from a console or test, against a written spec. Good design still matters, but working, readable, demoable code is the bar.

In practice the lines blur. Some LLD rounds ask for the core classes in code; some machine coding rounds end with a design discussion. Ask at the start: "Would you like me to focus on the class design, or should I get to runnable code?"

A step-by-step approach

1. Clarify requirements and scope

Spend the first five minutes asking questions, and write the answers down where the interviewer can see them. Which actors use the system? What are the three or four core operations? What is explicitly out of scope? Agree on a short list of functional requirements and one or two non-functional ones, such as "multiple entry gates can operate at the same time". This list is the contract you design against.

2. Identify the entities

Read your requirements and pull out the nouns. Most become candidate classes: vehicle, spot, floor, ticket, payment, gate. Some nouns turn out to be attributes rather than classes (a colour is a field, not an object). Some important classes are not nouns in the brief at all, such as a pricing strategy or a spot allocator; those appear when you assign responsibilities.

3. Assign responsibilities

For each class, say in one sentence what it knows and what it does. If the sentence needs "and" twice, the class is probably doing too much. This is where god classes are prevented: a ParkingLot that allocates spots, computes fees, prints tickets and processes card payments is four classes wearing one name.

4. Define relationships

Decide how classes relate. Is it inheritance (a Car is a Vehicle), composition (a ParkingLot owns its Floors, and they do not exist without it), aggregation (a Floor holds spots that could be moved elsewhere) or a plain association (a Ticket refers to a Vehicle)? Prefer composition over inheritance unless the "is-a" relationship is genuine and stable. If these terms feel rusty, the OOPs interview questions guide covers them.

5. Define interfaces

Identify the points where behaviour will vary and put an interface there: how a spot is chosen, how a fee is calculated, how payment is taken. Callers depend on the interface, not the concrete class, which is what lets you swap or add behaviour later.

6. Walk through the key flows

Pick the one or two most important flows and narrate them method by method: who calls whom, with what, and what changes state. This is where gaps show up, and it proves the design works rather than just looking tidy.

7. Add patterns only where they earn their place

Once the flows work, look for a specific problem a pattern solves: several interchangeable algorithms, object creation that depends on input, or state that changes what an object is allowed to do. Name the pattern and the problem together.

8. Discuss extensibility and edge cases

Ask yourself what the interviewer is likely to add next and show where it would go. Then list edge cases: the lot is full, a ticket is lost, payment fails, a vehicle leaves without paying.

9. Address concurrency where it is relevant

If two threads can touch the same object at once, say how you protect it. In a parking lot, two entry gates may try to assign the same spot. In a booking system, two users may pick the same seat. A sentence on locking or atomic operations here is often what separates a good answer from a strong one.

Worked example: a parking lot

Requirements

After clarifying, suppose you agree on these:

  • The lot has multiple floors, each with spots of three sizes: small, medium and large.
  • Vehicles are motorbikes, cars and trucks. A motorbike fits any spot, a car fits medium or large, a truck needs large.
  • A vehicle enters through a gate and receives a ticket with an entry time and assigned spot.
  • On exit, the fee is based on time parked and vehicle type, and payment is by cash or card.
  • Multiple entry gates operate concurrently.
  • Out of scope: reservations, monthly passes, a user-facing app.

Classes

  • Vehicle (abstract): fields licensePlate, type. Subclasses Motorbike, Car, Truck. An enum VehicleType is often enough; subclass only if types behave differently.
  • ParkingSpot: fields id, size (enum SpotSize), isFree, currentVehicle. Methods canFit(Vehicle v), assign(Vehicle v), release().
  • Floor: fields floorNumber, spots. Method findFreeSpot(Vehicle v), which delegates the choice to a strategy.
  • ParkingLot: fields floors, entryGates, exitGates. It is the entry point that coordinates, not the place where the logic lives.
  • Ticket: fields ticketId, vehicle, spot, entryTime, exitTime, status.
  • SpotAllocationStrategy (interface): ParkingSpot allocate(List<Floor> floors, Vehicle v). Implementations NearestFirstStrategy, LowestFloorFirstStrategy.
  • PricingStrategy (interface): Money calculate(Ticket t). Implementation HourlyPricingStrategy, with rates per vehicle type.
  • PaymentProcessor (interface): PaymentResult pay(Money amount). Implementations CashPayment, CardPayment.
  • EntryGate: method Ticket issueTicket(Vehicle v). ExitGate: method Receipt processExit(Ticket t, PaymentProcessor p).

Relationships

  • ParkingLot is composed of Floors, and each Floor is composed of ParkingSpots.
  • Ticket associates a Vehicle with a ParkingSpot for one visit.
  • EntryGate depends on SpotAllocationStrategy; ExitGate depends on PricingStrategy and PaymentProcessor. Both depend on interfaces, not concrete classes, so each strategy can be injected or swapped.

One flow: a car enters

  1. EntryGate.issueTicket(car) asks the injected SpotAllocationStrategy to allocate(floors, car).
  2. The strategy walks the floors and returns the first spot where canFit(car) is true and the spot is free.
  3. The gate calls spot.assign(car). This must be atomic: two gates can find the same free spot, so assign checks isFree and claims it inside one synchronised block (or a compare-and-set), returning false if another gate got there first, in which case the strategy tries again.
  4. The gate creates a Ticket with the spot and entryTime, stores it, and hands it to the driver. If no spot is found, it raises a LotFullException instead of issuing a ticket.

Exit is the mirror image: ExitGate.processExit sets exitTime, asks the PricingStrategy for the fee, charges it through the PaymentProcessor, and only on success calls spot.release() and marks the ticket paid.

Extending it: electric vehicle charging

The interviewer now asks for EV charging spots, billed per unit of electricity on top of parking time. In this design:

  • Add ChargingSpot as a ParkingSpot with a charger field, or add a features set to ParkingSpot, whichever keeps canFit simple.
  • Add an ElectricCar type, or a needsCharging flag on the vehicle, and a ChargingFirstStrategy that prefers charging spots for such vehicles.
  • Add an EvPricingStrategy that wraps the hourly strategy and adds the energy charge.

Nothing in EntryGate, ExitGate or Floor changes. That is the extension story interviewers are listening for: new behaviour arrives as new classes plugged into existing interfaces.

Which design patterns come up, and why

  • Strategy: several interchangeable algorithms behind one interface, such as pricing rules, spot allocation, or splitting methods in Splitwise. The most useful pattern in LLD rounds by a distance.
  • Factory: creation depends on input and callers should not know the concrete class, for example building the right Vehicle or PaymentProcessor from a type string.
  • Observer: one object's change must notify others without knowing who they are, such as a display board that updates free-spot counts, or notifications when a booking is confirmed.
  • State: an object's allowed actions depend on its current state. A vending machine that is idle, has money inserted, or is dispensing is the classic case; each state becomes a class, which removes long if-else chains.
  • Singleton: one shared instance, such as the ParkingLot itself. Use it with caveats: it is global state in disguise, it makes unit testing harder, and a naive lazy singleton is not thread-safe. Saying "I would pass a single instance in through the constructor rather than make it a singleton" usually scores better than reaching for the pattern.

The common mistake is pattern-stuffing: naming six patterns in the first ten minutes to sound senior. Every pattern adds indirection, and an interviewer will ask "what does this buy you here?" If the answer is not a concrete requirement, leave it out.

SOLID as a review checklist

Do not recite SOLID. Use it to check your design before you present it.

  • Single responsibility: can each class be described in one sentence?
  • Open/closed: does the next likely requirement mean adding a class, or editing several existing ones?
  • Liskov substitution: can every subclass replace its parent without surprises? If Truck.canPark() throws for half the spots, the hierarchy is wrong.
  • Interface segregation: does any class implement methods it does not need?
  • Dependency inversion: do high-level classes depend on interfaces (PricingStrategy) rather than concrete ones (HourlyPricingStrategy)?

Common LLD interview questions

  • Parking lot: spot-size matching, pricing rules and concurrent gates claiming the same spot.
  • Elevator system: the scheduling algorithm across multiple lifts, and modelling direction and state cleanly.
  • BookMyShow (movie ticket booking): holding seats temporarily while the user pays, without two users booking the same seat.
  • Splitwise: equal, exact and percentage splits behind one interface, and simplifying who owes whom.
  • Snake and ladder: a small game that tests whether you keep board, dice, players and rules separate and testable.
  • Rate limiter: choosing between token bucket, fixed window and sliding window, and keeping counters correct under concurrency.
  • LRU cache: O(1) get and put, which needs a hash map plus a doubly linked list, plus thread safety if asked.
  • Vending machine: state transitions, returning change, and handling an item running out mid-transaction.

Mistakes that cost the round

  • Coding before agreeing scope. You end up designing a different system from the one the interviewer has in mind.
  • God classes. One Manager or System class that holds everything is the single most common reason LLD rounds go badly.
  • Ignoring concurrency. If the problem involves shared resources such as seats, spots or counters, not mentioning race conditions looks like you have never run code in production.
  • No extension story. A design that works only for today's requirements has not been designed; it has been typed.
  • Going silent. The interviewer cannot score reasoning they cannot hear. If you get stuck, say what you know and reason from there; how to handle a question you don't know covers how.

How an LLD round is assessed

Interviewers are usually looking at four things: whether you clarified the problem before solving it, whether your classes have clear and separate responsibilities, whether the design extends without a rewrite, and whether you explained trade-offs as you went. A correct core flow matters more than covering every feature. In a machine coding round add working code, sensible tests and handling of invalid input. Language is rarely the point, but most candidates use Java or C++; if Java is your choice, Java interview practice helps you get fluent at expressing interfaces and enums quickly.

How to practise

Every few days, take one problem from the list above. Set a 45-minute timer, write the requirements, the class list and one flow, then ask yourself what a new requirement would break. Explaining it out loud matters as much as getting it right, because the round is a conversation. For the wider plan, see the IT interview preparation guide.

To rehearse that conversation, our system design interview practice track has Sia, an AI interviewer, ask one question at a time and follow up on what you actually said. You choose the difficulty (Beginner, Standard or Senior) and the round length, and get a scored written breakdown when you finish.

When you want a person instead, a live 40-minute Zoom interview with a working engineer scores you on technical depth (35%), problem solving (25%), communication (20%), behavioural (12%) and code quality (8%), and comes with a numbered certificate.