Skip to content
LLD Learn/Case Studies: Physical Systems
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Design a Parking Lot

10 min read

You'll learn to

  • -Model spots, levels, tickets, and vehicle types as a class hierarchy that stays open to new vehicle/spot types
  • -Handle concurrent spot assignment correctly so two cars never get the same spot

Design a Parking Lot is one of the most-asked LLD prompts precisely because it packs several fundamentals into one problem: a class hierarchy for varying vehicle/spot types, a concurrency-sensitive resource-assignment step, and a natural extensibility follow-up (add payment, add multiple entry gates). Working through it end to end ties together nearly everything covered so far in this course.

Step 1: Clarify Requirements

  • -What vehicle types does the lot support? (motorcycle, car, bus - each may need a different spot size)
  • -Does the lot have multiple levels/floors?
  • -Do we need to track payment, or just entry/exit and spot assignment?
  • -What happens when the lot is full? Should the design support parking spot reservation ahead of time, or only walk-up assignment?

Assume the answers: three vehicle types (Motorcycle, Car, Bus), multiple levels, each level has spots of different sizes, and payment is out of scope for this first pass (a natural place for the interviewer to follow up later).

Step 2: Identify Classes

The core class shape
from enum import Enum

class VehicleType(Enum):
    MOTORCYCLE = "motorcycle"
    CAR = "car"
    BUS = "bus"

class Vehicle(ABC):
    def __init__(self, license_plate: str):
        self.license_plate = license_plate
    @property
    @abstractmethod
    def type(self) -> VehicleType: ...

class Car(Vehicle):
    @property
    def type(self) -> VehicleType: return VehicleType.CAR

class SpotSize(Enum):
    SMALL = 1     # motorcycles only
    MEDIUM = 2    # cars (and motorcycles)
    LARGE = 3     # buses (and cars, and motorcycles)

class ParkingSpot:
    def __init__(self, spot_id: str, size: SpotSize):
        self.spot_id = spot_id
        self.size = size
        self.vehicle: Vehicle | None = None

    def can_fit(self, vehicle: Vehicle) -> bool:
        required = {VehicleType.MOTORCYCLE: SpotSize.SMALL,
                    VehicleType.CAR: SpotSize.MEDIUM,
                    VehicleType.BUS: SpotSize.LARGE}[vehicle.type]
        return self.size.value >= required.value

    def is_free(self) -> bool:
        return self.vehicle is None

Step 3: The Concurrency-Safe Assignment Logic

This is the part a naive solution gets wrong - finding a free spot and assigning it must be one atomic operation, exactly the check-then-act shape from the Concurrency module. A ParkingLevel owns its spots and a lock around the search-and-assign step.

Atomic search-and-assign, not a separate find() then assign()
import threading

class ParkingLevel:
    def __init__(self, spots: list[ParkingSpot]):
        self._spots = spots
        self._lock = threading.Lock()

    def park(self, vehicle: Vehicle) -> ParkingSpot | None:
        with self._lock:                              # find AND assign, atomically
            for spot in self._spots:
                if spot.is_free() and spot.can_fit(vehicle):
                    spot.vehicle = vehicle
                    return spot
            return None                                # this level is full for this vehicle type

class ParkingLot:
    def __init__(self, levels: list[ParkingLevel]):
        self._levels = levels

    def park_vehicle(self, vehicle: Vehicle) -> "Ticket | None":
        for level in self._levels:
            spot = level.park(vehicle)
            if spot:
                return Ticket(vehicle, spot)
        return None                                    # lot is full

Step 4: Ticket and Extensibility

A Ticket ties a Vehicle to the ParkingSpot it was assigned, plus an entry timestamp - the natural object to extend later with payment (duration * hourly_rate) without touching ParkingLevel or ParkingSpot at all. Notice the design has already absorbed the two most common follow-ups for free: "add a new vehicle type" means adding one VehicleType enum value and one Vehicle subclass, and "add payment" means extending Ticket, neither of which requires touching the atomic assignment logic in ParkingLevel.

A strong closing move on this problem: proactively mention the SpotSize.can_fit() logic uses ">=" so a motorcycle can use a MEDIUM or LARGE spot as a fallback when SMALL is full - a small detail that shows you thought about lot utilization, not just correctness.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

ScaleDojo Logo
Initializing ScaleDojo