HomeCoursesBlog
← Back to Blog
Article

Grokking the Low Level Design Interview: OOD Principles End to End

Grokking the Low Level Design Interview: OOD Principles End to End

TL;DR: The low level design round gives you one everyday system, such as a parking lot or an elevator, and asks for the classes behind it. The OOD principles are not decoration on that answer. OOD means object-oriented design, and each of its principles repairs one specific break in the obvious first design. This article works a parking lot from requirements to classes, and stops at every point where a principle changes the code.

Most guides list the SOLID principles, then separately design a parking lot. Nothing connects the two parts, so the principles read like vocabulary to memorize. They are not vocabulary. Each one exists because a plain design fails in a specific, repeatable way.

So this article connects them. One design, built once in the obvious way, then repaired principle by principle. You will see the requirements, the first class list, the exact line that breaks when a requirement changes, and the smaller design that survives.

What the low level design round asks for

The round goes by several names. Low level design (LLD), object-oriented design (OOD), and the machine coding round all describe the same 45 to 60 minutes. You are given a real-world thing. You produce classes, the relationships between them, and enough method bodies to show the design works.

It is not the coding round. Nobody is grading your algorithm against a time limit. It is not the system design round either, where you place caches and databases across many machines. This round stays inside one program and asks a narrower question: can you model a domain? A domain is the part of the real world that the software represents.

If you want the round mechanics and the four-step method first, read what the object-oriented design interview is and how to prepare. This article assumes you know the format and goes straight to the design.

The five principles, stated plainly

SOLID is an acronym for five design principles. Here is each one in a sentence you could say out loud in an interview.

PrincipleWhat it means
Single responsibilityA class has one reason to change.
Open/closedYou add behavior by adding code, not by editing working code.
Liskov substitutionA subclass can replace its parent without surprising the caller.
Interface segregationKeep interfaces small, so no class implements methods it ignores.
Dependency inversionCode depends on interfaces, not on concrete classes.

One term used throughout. An interface is a list of method names with no code behind them. A class that implements the interface promises to supply that code. Everything below turns on that idea.

The question: design a parking lot

Start by fixing the requirements. This costs two minutes and saves the whole design.

In scope:

  • The lot has several floors. Each floor has numbered spots.
  • Spots come in three sizes: motorcycle, compact, and large.
  • A vehicle gets a spot that fits it.
  • A ticket is issued at entry. A fee is charged at exit.
  • The fee depends on how long the vehicle stayed and what type it is.
  • A display board on each floor shows how many spots are free.

Out of scope, stated on purpose: the payment provider, reservations, and multiple lots in one city.

Naming what you are not building is not a formality. It sets the boundary of the model, and everything you design after this point is judged against that boundary.

Turning the nouns into objects

Read the requirements and collect the nouns. Most of them become classes.

ParkingLot, Floor, ParkingSpot, Vehicle, Ticket, DisplayBoard, and the panels at the entry and exit. Two more objects hide inside the verbs: something that assigns a spot, and something that calculates a fee.

That last sentence is where most candidates lose points. Verbs become objects too, and skipping that step is what produces the design below.

The parking lot requirements turned into objects, with the nouns becoming classes and two verbs, assign a spot and calculate a fee, becoming separate classes of their own

The first design, and the exact line that breaks

Here is what almost everyone writes first. It runs, and it is wrong in a way that only shows up on the follow-up question.

class ParkingLot:
    def park(self, vehicle):
        if vehicle.type == "motorcycle":
            spot = self.find_free_spot("motorcycle")
        elif vehicle.type == "compact":
            spot = self.find_free_spot("compact")
        elif vehicle.type == "large":
            spot = self.find_free_spot("large")
        spot.occupy(vehicle)
        return Ticket(vehicle, spot, now())

    def fee(self, ticket):
        hours = hours_between(ticket.issued_at, now())
        if ticket.vehicle.type == "motorcycle":
            return 1.0 * hours
        elif ticket.vehicle.type == "compact":
            return 2.0 * hours + 3.0
        elif ticket.vehicle.type == "large":
            return 3.5 * hours + 5.0

Now the interviewer adds one requirement: electric vehicles, which need a spot with a charger and are billed for the electricity used.

Count what you have to edit. The chain in park. The chain in fee. The display board, which counts spots by size. Three pieces of working code, all edited for one new type. Every edit is a chance to break parking, which had nothing to do with the change.

There is a second problem, harder to notice than the first. ParkingLot decides where vehicles go and decides what they cost. Those two things change for unrelated reasons. A new spot size changes one. A weekend price changes the other. That is two reasons to change in one class.

Each principle repairs one break

Now the principles stop being vocabulary. Take them in the order the damage appears.

Single responsibility splits the class. ParkingLot keeps one job, which is coordinating. Spot assignment moves to a SpotAssigner. Pricing moves out to its own family of classes. Each new class now has one reason to change, and the pricing change can no longer break parking.

Dependency inversion and open/closed remove the if chain. Instead of asking what type a vehicle is, define an interface for pricing and write one small class per type.

from abc import ABC, abstractmethod


class FeeStrategy(ABC):
    @abstractmethod
    def fee(self, ticket) -> float:
        ...


class MotorcycleFee(FeeStrategy):
    def fee(self, ticket):
        return 1.0 * ticket.hours()


class CompactFee(FeeStrategy):
    def fee(self, ticket):
        return 2.0 * ticket.hours() + 3.0


FEES = {
    VehicleType.MOTORCYCLE: MotorcycleFee(),
    VehicleType.COMPACT: CompactFee(),
}


class Cashier:
    def __init__(self, fees):
        self.fees = fees                      # an interface, not a concrete class

    def charge(self, ticket):
        return self.fees[ticket.vehicle.type].fee(ticket)

Adding electric vehicles is now one new class and one new line in FEES. No working code is edited. That sentence is what open/closed means, and saying it in those words is worth more than naming the principle.

Cashier also never mentions MotorcycleFee or CompactFee. It holds the interface. That is dependency inversion, and the practical payoff is that you can test Cashier with a fake fee that always returns 5.

Liskov substitution decides your inheritance. The tempting move is to make ElectricSpot a subclass of ParkingSpot that rejects vehicles without a charging port. Do not. Any code holding a ParkingSpot expects a fitting vehicle to be accepted, and the subclass would break that expectation. Model the charger as a property of the spot instead, and let the assigner read it.

The test to apply is short: if a subclass has to weaken a promise the parent made, it should not be a subclass.

Interface segregation shrinks what the display board sees. The board needs one method, which is the count of free spots on a floor. Give it a small interface with that method, not a reference to the whole lot. A class that can only read a count cannot accidentally park a car.

The if-chain design on the left, where one new vehicle type forces edits in three places, next to the strategy design on the right, where the same change is one new class and one map entry

The structure that results

Six classes, each with one job.

ClassIts one reason to change
ParkingLotThe set of floors changes.
FloorHow spots are grouped changes.
ParkingSpotWhat a spot can hold changes.
SpotAssignerThe rule for choosing a spot changes.
FeeStrategy and its implementationsA price changes.
DisplayBoardWhat the sign shows changes.

Compare that with the first version, where a price change and a spot-size change touched the same method. The design did not get bigger. It got separated.

Want the whole round taught this way? Grokking the Object-Oriented Design Interview works 16 complete case studies end to end, each with requirements, class diagrams, and code, including the parking lot, the elevator system, and the vending machine.

The three patterns that appear in almost every question

A design pattern is a named solution to a problem that keeps coming back. Three of them cover most low level design questions.

Strategy puts each version of an algorithm in its own class, and picks one at runtime. The FeeStrategy above is exactly this. Use it whenever you see a chain of if statements branching on a type.

Factory puts object creation in one place, so callers never name a concrete class. Use it when the type to build is decided by input data, such as building the right Vehicle from a scanned plate.

Observer lets objects register to be told when something changes. The display board registers with its floor, and the floor tells it when a spot is taken or freed. Use it when one change has to reach several listeners that should not know about each other.

One warning on a fourth. Singleton, which allows only one instance of a class, is the pattern candidates name most often and the one that helps least. It makes testing harder, because the single instance carries state between tests. If you use it for the lot itself, be ready to explain why a global is acceptable there.

A practice ladder

Six questions in order. Each one adds a kind of complexity the previous one did not have.

  1. Parking lot. Spot types and pricing. The design above.
  2. Vending machine. Your first state machine, where the same button means different things depending on what happened before.
  3. Elevator system. Several machines running at once, plus a scheduling rule you must defend.
  4. Library lending. Users, holds, and due dates, which forces real rules about who may do what.
  5. Ride sharing. Matching two sides of a market, and the first design where location matters.
  6. Chess or another board game. Many piece types, which is the hardest test of inheritance and Liskov substitution.

Design each one on paper first, then write only the classes and the two or three methods that carry the logic. Producing every getter is not what is being measured.

The takeaway

The low level design round is not a memory test on five acronyms. It is one question asked repeatedly: when this requirement changes, how much working code do you have to edit? A design where a new vehicle type means one new class and one new line is a good design. A design where it means three edits in three files is not, however correct it is today.

Work the parking lot until you can build the second version directly. Then take the practice ladder in order. The full round mechanics and what gets graded are in the object-oriented design interview guide, and if you are also preparing for the algorithm round, the complete list of coding patterns covers that side.

Go deeper: Grokking the Object-Oriented Design Interview teaches 16 case studies with full class diagrams and code. For the algorithm round, Grokking the Coding Interview covers all 42 patterns for a one-time $79 with lifetime access.

Frequently asked questions

Is low level design the same as object-oriented design? In interview usage, yes. Low level design, LLD, object-oriented design, OOD, and the machine coding round all name the same round. Companies in India tend to say LLD or machine coding. Companies in the United States tend to say object-oriented design. The question you get is the same.

Do I have to name the SOLID principles out loud? Naming them is not the point, and reciting all five without applying any is a weak answer. Describe the effect instead. Saying "a new vehicle type is one new class and no edits here" shows open/closed better than saying "open/closed".

Which language should I use for a low level design interview? Use the one you write fastest in. Java and C++ make interfaces and access rules visible, which suits this round. Python is quicker to write and is accepted everywhere, and abc gives you real abstract classes. Ask if you are unsure, because some machine coding rounds fix the language.

How much code do I actually have to write? Class definitions, the relationships between them, and the two or three methods that carry the real logic. Skip getters, setters, and boilerplate unless asked. Say that you are skipping them, so it reads as a choice.

What is the most common mistake in this round? Naming classes before fixing the requirements. A design built on an unstated assumption fails as soon as the interviewer adds the requirement you assumed away. Spend the first two minutes agreeing on scope, and say out loud what you are leaving out.

Are design patterns required, or is SOLID enough? SOLID tells you when a design is wrong. Patterns give you the standard repair. You can pass by applying the principles and never naming a pattern, but knowing Strategy, Factory, and Observer means you reach the repair faster.

Grokking the Coding Interview
One-Stop Portal For Coding Interviews.
Follow us:
Copyright © 2025 Coding Interview All rights reserved.