Compile Ready
All low level design problems
Low Level Design/Beginner/Core Systems

Design a Parking Lot

The canonical LLD warm-up — multi-level lots, vehicle/spot types, ticketing, and pluggable pricing.

Beginner 45m interview 10m read Very High frequency Popularity 99
Strategy Factory Method Singleton Amazon Google Microsoft Flipkart

Problem Statement

Design the object model for a multi-level parking lot that a facility operator can run day to day. Vehicles of different sizes enter, are assigned a suitable free spot, receive a ticket, and pay a fee computed from how long they stayed when they exit.

The interview is about the class model — responsibilities, relationships, and the seams that keep the design extensible for new vehicle types, spot types, and pricing rules — not about a database schema or a REST API.

Business context

Parking Lot is the canonical warm-up LLD question at Amazon, Google, and most product companies. It is deceptively rich: it exercises enums, an inheritance hierarchy, composition (Lot → Level → Spot), the Strategy pattern for pricing, a Factory for object creation, and a real concurrency concern (two cars racing for the last spot). Interviewers use it to see whether you can keep responsibilities in the right place and defend your boundaries.

Functional Requirements

  • Support multiple levels, each with a fixed set of spots.

  • Spots come in sizes (motorcycle, compact, large); a vehicle may occupy any spot that can hold its size.

  • Park a vehicle: find and reserve a suitable free spot, then issue a ticket.

  • Unpark a vehicle by ticket: free the spot and compute the fee for the stay.

  • Report real-time availability of free spots by size, per level.

  • Reject entry gracefully when the lot is full for the requested vehicle size.

Non-Functional Requirements

Correctness under concurrency

Two entry gates must never assign the same spot. Park/unpark on the lot are the critical section.

Extensibility

New vehicle types, spot sizes, and pricing plans should slot in without editing the parking flow.

Low latency

A park request is a small in-memory scan — effectively O(spots on a level) and interactive.

Encapsulation

Fit rules and occupancy state live on the domain objects, not in the controller orchestrating them.

Requirement Clarification

QCan a large vehicle occupy multiple small spots?

Out of scope for the base design — one vehicle takes exactly one spot that can hold its size. Multi-spot allocation is a documented extension.

QIs pricing hourly, daily, or tiered?

Assume a pluggable pricing plan. The base implementation ships an hourly strategy, but the exit path must not hard-code it.

QDo we need to model payments, gates, or an entry display board?

Not for the core model. We expose park, unpark, and availability; payment/hardware are adapters layered on top.

QAre spot assignments persisted across restarts?

In-memory is fine for the interview. We keep state on the objects and note where a repository would plug in for durability.

UML Class Diagram

Rendering diagram…
Composition runs top-down (Lot owns Levels, Level owns Spots); pricing is an interface the Lot depends on, so a new plan never touches the parking flow.

Sequence Diagram

Rendering diagram…
The Lot orchestrates but delegates the spot decision to the Level and the fee decision to the strategy — it owns coordination, not policy.

Entity Identification

ParkingLot

Aggregate root. Owns levels + the fee strategy, keeps the map of active tickets, and is the single synchronized entry point for park/unpark.

levelsfeeStrategyactiveTickets

Level

Owns the spots on one floor and knows how to find + reserve the first spot that fits a vehicle.

numberspots

ParkingSpot

Holds occupancy state and the fit rule (canFit). Guards its own invariant — you cannot park in an occupied or too-small spot.

idsizevehicle

Vehicle

Abstract base carrying plate + size; Motorcycle/Car/Bus fix the size. Knows whether it fits a given spot size.

platesize

Ticket

Immutable record of a parking session: the reserved spot, the vehicle, and the entry timestamp used for billing.

idspotvehicleentryTime

ParkingFeeStrategy

Pricing policy interface. HourlyFeeStrategy is the default; new plans (daily, weekend, EV) implement the same method.

calculateFee(ticket, exitTime)

VehicleSize

Ordered enum (MOTORCYCLE < COMPACT < LARGE) whose ordinal encodes the fit rule via canHold.

MOTORCYCLECOMPACTLARGE

Design Patterns Used

Strategy

ParkingFeeStrategy isolates pricing. The exit path calls calculateFee without knowing whether it is hourly, daily, or promotional — a new plan is a new class, not an edit.

Factory Method

ParkingFactory centralises vehicle + spot creation so tests, demos, and gates build objects consistently and the concrete Motorcycle/Car/Bus types stay package-private.

Template Method

The abstract Vehicle fixes the plate/size skeleton and the fitsIn rule; subclasses only supply their size, avoiding duplicated fit logic.

Step-by-Step Design

  1. 1Model sizes and put the fit rule in one place

    Use an ordered VehicleSize enum and express compatibility as canHold on it. Every other class asks the enum instead of re-deriving the rule.

    public enum VehicleSize {
        MOTORCYCLE, COMPACT, LARGE;
        public boolean canHold(VehicleSize needed) {
            return ordinal() >= needed.ordinal();
        }
    }
  2. 2Let the spot guard its own state

    ParkingSpot.canFit checks both free and big enough; park/unpark throw on invariant violations so no caller can corrupt occupancy.

    public boolean canFit(Vehicle candidate) {
        return isFree() && candidate.fitsIn(size);
    }
  3. 3Let each level own spot selection

    Level.reserveSpotFor scans its spots, reserves the first fit, and returns an Optional. The lot never iterates raw spots.

  4. 4Issue tickets at the lot boundary, atomically

    ParkingLot.park tries levels in order and only creates a Ticket after a spot is reserved. park/unpark are synchronized so the check-then-assign cannot race.

  5. 5Price exits behind a strategy

    unpark validates the ticket, removes it, asks the injected ParkingFeeStrategy for the fee, then frees the spot. Pricing is swappable at construction time.

Complete Java Implementation

Loading…

Explanation of Every Class

VehicleSize

An ordered enum. Because LARGE > COMPACT > MOTORCYCLE by ordinal, canHold is a one-liner and the fit rule lives in exactly one place.

Vehicle / Motorcycle / Car / Bus

Vehicle validates the plate and stores the size; fitsIn delegates to VehicleSize.canHold. The three subclasses only pin their size, so there is no duplicated logic.

ParkingSpot

Holds id, size, and the current vehicle. canFit combines free + size checks; park/unpark enforce the invariant and throw rather than silently misbehave.

Level

Owns a floor's spots. reserveSpotFor does the linear scan-and-reserve; freeSpotsBySize powers the availability board using an EnumMap.

Ticket

Immutable session record created with a random id and the entry Instant. It ties a vehicle to its reserved spot for billing on exit.

ParkingFeeStrategy / HourlyFeeStrategy

The interface is the pricing seam. HourlyFeeStrategy rounds partial hours up and multiplies by a cents-per-hour rate; other plans implement the same method.

ParkingFactory

Static factory that maps a type string to the right Vehicle subclass and builds spot ids consistently. Keeps the concrete vehicle classes package-private.

ParkingLot

The aggregate root. park/unpark/availability are synchronized; it owns the active-ticket map and delegates spot selection to levels and pricing to the strategy.

Dry Run

Sample input

Lot with 1 level: spots [MOTORCYCLE, COMPACT, LARGE]. Actions: park a Car, park a Bus, unpark the Car after 90 minutes at 200¢/hour.

StepActionSpot chosenActive ticketsResult
1park(Car · COMPACT)COMPACT spot{T1}Ticket T1
2park(Bus · LARGE)LARGE spot{T1, T2}Ticket T2
3unpark(T1) @ +90 minCOMPACT freed{T2}ceil(90/60)=2h × 200¢ = 400¢
4availability(){T2}COMPACT:1, MOTORCYCLE:1, LARGE:0

Step 3 shows the hourly strategy rounding 90 minutes up to 2 hours, and freeing the spot the moment the ticket is settled.

Complexity Analysis

OperationTimeSpaceNote
parkO(L × S)O(1)L levels, S spots per level — a linear scan for the first fit.
unparkO(1)O(1)Map removal + a constant-time fee computation.
availabilityO(L × S)O(L)Counts free spots by size across every level.

Park is the hot path. If S grows large, replace the per-level linear scan with size-bucketed free-spot queues to make park O(1) as well (see Alternative Designs).

Extensibility

New vehicle / spot size

Add an enum constant (respecting order) and, if needed, a Vehicle subclass. The fit rule and every scan keep working.

New pricing plan

Implement ParkingFeeStrategy (daily cap, weekend rate, EV surcharge) and inject it — no change to park/unpark.

Reservations / EV charging

Add a SpotFeature set or a decorator on ParkingSpot; canFit grows an extra predicate without touching the lot.

Multiple entrances / durability

Swap the in-memory ticket map for a TicketRepository; the synchronized methods become the transaction boundary.

Alternative Designs

Size-bucketed free-spot queues

Keep a per-size map (VehicleSize to a deque of free spots) per level so park pops in O(1) instead of scanning.

Tradeoffs

Faster park at the cost of extra bookkeeping on every park/unpark and more state to keep consistent under concurrency.

Central **SpotManager** service

Pull spot selection out of Level into a dedicated allocator that sees all levels at once (e.g. to balance load).

Tradeoffs

Enables global policies but weakens the clean Lot→Level→Spot ownership and centralises a lock.

Lock-per-level instead of lock-on-lot

Synchronize each Level so cars entering different floors don't contend on one lock.

Tradeoffs

Higher throughput, but the availability snapshot is no longer a single atomic view.

Common Mistakes

  • ×

    Putting the fit rule in the controller (if size == ...) instead of on VehicleSize/ParkingSpot.

  • ×

    Creating the ticket before a spot is actually reserved, so a failed park still bills the driver.

  • ×

    Forgetting to synchronize park/unpark, letting two gates hand out the same spot.

  • ×

    Hard-coding hourly pricing in the exit path instead of behind a strategy.

  • ×

    Exposing mutable internal lists (spots, tickets) so callers can corrupt occupancy state.

  • ×

    Modelling vehicle types with an enum + switch instead of polymorphism, which then leaks into every method.

Follow-up Interview Questions

QHow do you make park O(1) under heavy load?

Maintain per-size free-spot queues per level and pop the head; push back on unpark. Trades a little memory + bookkeeping for constant-time allocation.

QHow would you support parking a large vehicle across two spots?

Introduce a SpotGroup/allocation abstraction so a reservation can hold N adjacent spots atomically; canFit becomes a group-level check.

QTwo entrances race for the last spot — what happens?

Because park is synchronized (or the level lock is held during check-then-reserve), the reservation is atomic; the loser gets Optional.empty() and is told the lot is full.

QWhere would payments and gate hardware live?

As adapters at the boundary — a PaymentProcessor and gate controllers call into park/unpark. The domain stays free of I/O.

Production Considerations

Persistence

Back tickets + occupancy with a TicketRepository (SQL) so state survives restarts and supports multiple app instances.

Concurrency at scale

A single lock becomes a bottleneck. Move to per-level locks or optimistic reservation with a DB row lock on the spot.

Observability

Emit metrics for occupancy %, park failures, and average dwell time; alert when a size class is chronically full.

Pricing correctness

Compute fees server-side with a fixed clock source, round consistently, and keep an audit trail of the rate applied to each ticket.

What Interviewers Look For

  • Did you keep the fit rule in one place and out of the orchestration code?

  • Is object creation atomic — no ticket without a reserved spot?

  • Did you name the concurrency problem and guard the critical section?

  • Is pricing behind an interface so a new plan is additive?

  • Can you extend to new vehicle/spot types by adding data, not editing flow?

Quiz

0/4 answered

  1. 1.Why is **canHold** defined on **VehicleSize** rather than in **ParkingLot**?

  2. 2.What does making **ParkingFeeStrategy** an interface buy you?

  3. 3.Why are **park** and **unpark** synchronized?

  4. 4.Why create the **Ticket** only after a spot is reserved?

Practice Variants

Add EV charging spots

Intermediate

Introduce a spot feature so only EVs may take charging spots, and prefer non-charging spots for non-EVs. Keep the fit rule extensible.

Daily-cap pricing

Beginner

Add a strategy that charges hourly but never more than a daily maximum. Verify the exit path needs no changes.

Nearest-spot allocation

Advanced

Allocate the free spot closest to an entrance. Decide whether this belongs in Level, a new allocator, or a comparator.

Flashcards

Cheat Sheet

Entities: ParkingLot → Level → ParkingSpot; Vehicle (abstract) + Motorcycle/Car/Bus; Ticket; ParkingFeeStrategy; VehicleSize.

Patterns: Strategy (pricing), Factory (creation), Template Method (Vehicle base).

Flows: park = try levels → reserve first fit → issue ticket; unpark = validate ticket → compute fee → free spot.

Invariants: one vehicle per spot; ticket only after reservation; park/unpark atomic.

Complexity: park O(L×S), unpark O(1), availability O(L×S).

Extend: new size = enum constant; new price = new strategy; reservations = spot feature/decorator.

References

  • BookHead First Design Patterns (Strategy, Factory)Freeman & Robson
  • BookEffective Java — Item 34 (enums), Item 1 (static factories)Joshua Bloch
  • DocsRefactoring Guru — Strategy Pattern