Chapter 10 · Article 44 of 55

Parking Lot System Design

"Design an automated parking lot system for a multi-floor commercial building. The system should manage vehicle entry and exit, assign parking spots based on vehicle type, issue…

Article outline13 sections on this page

Problem Statement

"Design an automated parking lot system for a multi-floor commercial building. The system should manage vehicle entry and exit, assign parking spots based on vehicle type, issue tickets, track parking duration, and process payments. The lot has multiple entry and exit points, a display board showing real-time availability, and supports different vehicle sizes."

This is one of the most frequently asked Low-Level Design (LLD) interview problems. The interviewer expects you to identify entities, define relationships, handle concurrency, and demonstrate extensibility - all within a clean object-oriented design.

The parking lot problem tests your ability to:

  • Model real-world entities into classes and interfaces
  • Apply appropriate design patterns
  • Handle concurrent access to shared resources
  • Design for extensibility without over-engineering
  • Think about edge cases and failure scenarios
  • Communicate trade-offs clearly

Why interviewers love this problem: It has a well-defined physical analogy everyone understands, yet it contains enough complexity (concurrency, multiple entity types, payment processing, real-time updates) to differentiate strong candidates. A weak answer produces a flat list of classes. A strong answer demonstrates layered thinking - from high-level architecture down to thread-safety at the spot level.


Requirements

Functional Requirements

  1. Park a vehicle - Assign an available parking spot to an incoming vehicle based on its type and size.
  2. Unpark a vehicle - Free the spot when the vehicle leaves and update availability.
  3. Find available spot - Locate the nearest available spot for a given vehicle type on any floor.
  4. Issue a ticket - Generate a parking ticket at entry with timestamp, assigned spot, and vehicle details.
  5. Calculate parking fee - Compute the fee based on duration, vehicle type, and rate structure.
  6. Process payment - Accept payment via cash, credit card, or UPI at exit panels.
  7. Support multiple vehicle types - Handle motorcycles, cars, buses, and trucks with different spot sizes.
  8. Multiple floors - Manage spots across multiple parking floors with independent tracking.
  9. Multiple entry/exit points - Support concurrent vehicle entry and exit at different gates.
  10. Display real-time availability - Show available spots per floor and per vehicle type on display boards.

Non-Functional Requirements

  1. Concurrency - Multiple vehicles entering/exiting simultaneously must not cause double-allocation of spots.
  2. Scalability - The design should support adding new floors, spots, or entry/exit points without major refactoring.
  3. Real-time tracking - Availability information must update within seconds of a park/unpark event.
  4. Fault tolerance - Ticket and payment data must persist across power outages or system restarts.
  5. Low latency - Spot allocation and fee calculation should complete in constant or logarithmic time.

Constraints & Assumptions

ParameterValue
Number of floors5
Spots per floor200 (80 compact, 80 regular, 20 large, 10 motorcycle, 10 handicapped)
Total capacity1,000 spots
Vehicle typesMotorcycle, Car, Truck/Bus
Spot typesMotorcycle, Compact, Regular, Large, Handicapped, EV Charging
Entry points3
Exit points3
Payment methodsCash, Credit Card, UPI
Rate structureHourly (first hour flat, then per-hour increments)
Operating hours24/7

Assumptions:

  • One vehicle occupies exactly one spot (trucks/buses use Large spots).
  • Handicapped spots are reserved and require validation.
  • EV charging spots have an additional per-hour surcharge.
  • A vehicle cannot re-enter without first completing payment and exiting.
  • Ticket is the source of truth for parking duration.

Key Entities & Relationships

Entities

EntityResponsibility
ParkingLotTop-level singleton managing the entire system
ParkingFloorManages spots on a single floor
ParkingSpotAbstract base - subtypes: MotorcycleSpot, CompactSpot, RegularSpot, LargeSpot, HandicappedSpot, EVSpot
VehicleAbstract base - subtypes: Motorcycle, Car, Truck, Bus
TicketIssued at entry; holds entry time, spot reference, vehicle info
PaymentProcesses fee calculation and payment transaction
EntryPanelHandles vehicle arrival, ticket issuance, spot assignment
ExitPanelHandles ticket scanning, fee display, payment processing
DisplayBoardShows real-time availability per floor
ParkingRateEncapsulates pricing strategy per vehicle type

Relationships

  • ParkingLot has-many ParkingFloor (composition)
  • ParkingFloor has-many ParkingSpot (composition)
  • ParkingLot has-many EntryPanel, ExitPanel, DisplayBoard (composition)
  • Ticket references ParkingSpot and Vehicle (association)
  • Payment belongs-to Ticket (association)
  • Vehicle hierarchy uses inheritance (Motorcycle, Car, Truck extend Vehicle)
  • ParkingSpot hierarchy uses inheritance (CompactSpot, RegularSpot, etc. extend ParkingSpot)
  • DisplayBoard observes ParkingFloor for availability changes

ASCII Class Diagram

┌─────────────────────────────────────────────────────────────────────────┐
│                            ParkingLot (Singleton)                        │
├─────────────────────────────────────────────────────────────────────────┤
│ - id: String                                                            │
│ - name: String                                                          │
│ - floors: List<ParkingFloor>                                            │
│ - entryPanels: List<EntryPanel>                                         │
│ - exitPanels: List<ExitPanel>                                           │
│ - displayBoards: List<DisplayBoard>                                     │
│ - activeTickets: Map<String, Ticket>                                    │
├─────────────────────────────────────────────────────────────────────────┤
│ + getInstance(): ParkingLot                                             │
│ + addFloor(floor): void                                                 │
│ + findAvailableSpot(vehicleType): ParkingSpot                           │
│ + parkVehicle(vehicle, entryPanel): Ticket                              │
│ + unparkVehicle(ticket, exitPanel): Payment                             │
│ + isFull(): boolean                                                     │
└─────────────────────────────────────────────────────────────────────────┘
                          │ composition (1:N)
                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                            ParkingFloor                                  │
├─────────────────────────────────────────────────────────────────────────┤
│ - floorNumber: int                                                      │
│ - spots: Map<SpotType, List<ParkingSpot>>                               │
│ - displayBoard: DisplayBoard                                            │
├─────────────────────────────────────────────────────────────────────────┤
│ + getAvailableSpot(vehicleType): ParkingSpot                            │
│ + getAvailableCount(spotType): int                                      │
│ + notifyDisplayBoard(): void                                            │
└─────────────────────────────────────────────────────────────────────────┘
                          │ composition (1:N)
                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                        ParkingSpot (Abstract)                            │
├─────────────────────────────────────────────────────────────────────────┤
│ - spotId: String                                                        │
│ - floorNumber: int                                                      │
│ - spotType: SpotType                                                    │
│ - status: SpotStatus {FREE, OCCUPIED, RESERVED, OUT_OF_SERVICE}         │
│ - vehicle: Vehicle                                                      │
├─────────────────────────────────────────────────────────────────────────┤
│ + assignVehicle(vehicle): boolean                                       │
│ + removeVehicle(): Vehicle                                              │
│ + isFree(): boolean                                                     │
│ + canFitVehicle(vehicleType): boolean                                   │
└─────────────────────────────────────────────────────────────────────────┘
        ▲ inheritance
        │
        ├── MotorcycleSpot
        ├── CompactSpot
        ├── RegularSpot
        ├── LargeSpot
        ├── HandicappedSpot
        └── EVChargingSpot (+ chargingRate: double)

┌─────────────────────────────────────────────────────────────────────────┐
│                          Vehicle (Abstract)                              │
├─────────────────────────────────────────────────────────────────────────┤
│ - licensePlate: String                                                  │
│ - vehicleType: VehicleType {MOTORCYCLE, CAR, TRUCK, BUS}                │
│ - color: String                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│ + getType(): VehicleType                                                │
└─────────────────────────────────────────────────────────────────────────┘
        ▲ inheritance
        │
        ├── Motorcycle
        ├── Car
        ├── Truck
        └── Bus

┌─────────────────────────────────────────────────────────────────────────┐
│                              Ticket                                      │
├─────────────────────────────────────────────────────────────────────────┤
│ - ticketId: String                                                      │
│ - entryTime: DateTime                                                   │
│ - exitTime: DateTime                                                    │
│ - vehicle: Vehicle                                                      │
│ - spot: ParkingSpot                                                     │
│ - status: TicketStatus {ACTIVE, PAID, LOST}                             │
├─────────────────────────────────────────────────────────────────────────┤
│ + getParkedDuration(): Duration                                         │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                              Payment                                     │
├─────────────────────────────────────────────────────────────────────────┤
│ - paymentId: String                                                     │
│ - ticket: Ticket                                                        │
│ - amount: double                                                        │
│ - method: PaymentMethod {CASH, CREDIT_CARD, UPI}                        │
│ - status: PaymentStatus {PENDING, COMPLETED, FAILED, REFUNDED}          │
│ - timestamp: DateTime                                                   │
├─────────────────────────────────────────────────────────────────────────┤
│ + processPayment(): boolean                                             │
│ + calculateAmount(ticket, rate): double                                 │
└─────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────┐       ┌──────────────────────────┐
│       EntryPanel         │       │        ExitPanel         │
├──────────────────────────┤       ├──────────────────────────┤
│ - panelId: String        │       │ - panelId: String        │
│ - location: String       │       │ - location: String       │
├──────────────────────────┤       ├──────────────────────────┤
│ + issueTicket(vehicle)   │       │ + scanTicket(ticket)     │
│ + printTicket(ticket)    │       │ + processPayment(ticket) │
└──────────────────────────┘       │ + openGate()             │
                                   └──────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                          DisplayBoard (Observer)                         │
├─────────────────────────────────────────────────────────────────────────┤
│ - floorNumber: int                                                      │
│ - availableMotorcycle: int                                              │
│ - availableCompact: int                                                 │
│ - availableRegular: int                                                 │
│ - availableLarge: int                                                   │
├─────────────────────────────────────────────────────────────────────────┤
│ + update(floor): void                                                   │
│ + show(): void                                                          │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                     ParkingRate (Strategy Interface)                     │
├─────────────────────────────────────────────────────────────────────────┤
│ + calculateFee(duration, vehicleType): double                           │
└─────────────────────────────────────────────────────────────────────────┘
        ▲ implements
        │
        ├── HourlyRate
        ├── FlatRate
        └── TieredRate (weekday/weekend/peak hours)

Design Decisions & Trade-offs

1. Spot Allocation Strategy

StrategyProsCons
Nearest to entryBetter UX, less driving timeUneven wear on near-entry spots; complex to compute
RandomEven distribution, simplePoor UX, longer search time
Floor-based (fill one floor at a time)Simple tracking, efficient displayLower floors always congested

Recommended: Nearest-to-entry within the lowest available floor. Use a priority queue (min-heap) per floor keyed by distance from entry. This gives O(log n) allocation time.

2. Payment Calculation

ModelUse Case
HourlyStandard commercial lots - charge per hour with first-hour minimum
Flat rateEvent parking, airport long-term
TieredPeak/off-peak pricing, weekend surcharges

Recommended: Use the Strategy pattern so the rate model is interchangeable. Default to hourly with ceiling (round up partial hours).

3. Concurrency Handling for Spot Allocation

This is the critical design challenge. Two vehicles arriving simultaneously at different entry panels must not be assigned the same spot.

Approaches:

  • Synchronized block / Mutex - Lock the spot assignment method. Simple but creates a bottleneck.
  • Compare-and-Swap (CAS) - Optimistic locking on spot status. Attempt to set status from FREE to OCCUPIED atomically; retry on failure.
  • Database-level locking - Use SELECT FOR UPDATE or optimistic versioning if backed by a database.

Recommended: CAS with retry at the spot level. This allows concurrent allocation across different spots while preventing double-booking of the same spot. Lock granularity is per-spot, not per-floor or per-lot.

assignVehicle(vehicle):
    if CAS(status, FREE, OCCUPIED):
        this.vehicle = vehicle
        return true
    return false  // spot was taken, caller retries with next spot

4. Single vs Multiple Entry/Exit Points

Multiple entry/exit points improve throughput but add complexity:

  • Each panel operates independently and communicates with the central ParkingLot instance.
  • Spot allocation must be thread-safe (see above).
  • Ticket validation at exit must work regardless of which entry was used.

Recommended: Stateless panels that delegate all logic to the central ParkingLot service. Panels are thin clients.

5. Real-time Availability Display

  • Push model (Observer): ParkingFloor notifies DisplayBoard on every park/unpark event. Immediate updates but high message volume.
  • Pull model (Polling): DisplayBoard queries floor counts every N seconds. Simpler but slightly stale.

Recommended: Observer pattern with batched updates. Aggregate changes over a 1-second window before pushing to the display. This balances freshness with performance.


Design Patterns Used

PatternApplicationBenefit
SingletonParkingLotSingle point of coordination; prevents multiple instances managing the same physical lot
StrategyParkingRate (HourlyRate, FlatRate, TieredRate)Swap pricing algorithms without modifying client code
Factory MethodVehicleFactory, SpotFactoryEncapsulate creation logic; decouple client from concrete classes
ObserverDisplayBoard observes ParkingFloorReal-time UI updates without tight coupling
StateParkingSpot status transitions (FREE → OCCUPIED → RESERVED → OUT_OF_SERVICE)Clean state machine; prevents invalid transitions
Template MethodPayment processing (validate → calculate → charge → confirm)Enforce processing steps while allowing method-specific variation
RepositoryTicketRepository, SpotRepositoryAbstract persistence layer; swap between in-memory and database

Pattern Interaction

These patterns don't exist in isolation. Here's how they interact in a typical flow:

  1. Vehicle arrives at EntryPanelFactory creates the Vehicle object from input.
  2. Singleton ParkingLot is called to find a spot → internally iterates floors.
  3. State pattern on ParkingSpot validates the FREE → OCCUPIED transition atomically.
  4. Observer pattern triggers DisplayBoard update after spot status changes.
  5. At exit, Strategy pattern selects the correct ParkingRate implementation.
  6. Template Method in Payment ensures validate → calculate → charge → confirm sequence regardless of payment method.

Edge Cases

1. Lot Full

  • findAvailableSpot() returns null → EntryPanel displays "LOT FULL" and does not raise the gate.
  • Display boards at the entrance road show full status to prevent vehicles from queuing.

2. Payment Failure

  • Payment status set to FAILED → gate does not open.
  • Offer retry with alternative payment method.
  • After 3 failures, route to manual assistance booth.

3. Vehicle Overstays

  • If a vehicle exceeds 24 hours, apply daily maximum cap rate.
  • After 72 hours, flag for towing and notify security.

4. Power Outage / System Crash

  • Tickets and spot assignments must be persisted to durable storage (database/disk).
  • On recovery, rebuild in-memory state from the last persisted snapshot.
  • Physical barrier gates default to closed position (fail-secure).

5. Concurrent Spot Allocation Race Condition

  • Two entry panels simultaneously try to assign the last available compact spot.
  • CAS ensures only one succeeds; the other retries and either finds another spot or reports full.

6. Handicapped Spots

  • Require handicapped permit validation before assignment.
  • If all handicapped spots are taken, offer a regular spot with accessible path.
  • Never assign handicapped spots to non-permitted vehicles, even if the lot is nearly full.

7. EV Charging Spots

  • Track charging state separately (CHARGING, CHARGED, IDLE).
  • Apply surcharge for charging duration.
  • Notify vehicle owner when charging is complete to free the spot for others.

8. Lost Ticket

  • Charge maximum daily rate as penalty.
  • Verify vehicle via license plate camera records at entry.
  • Issue a replacement ticket for exit processing.

Extensibility Points

Adding New Vehicle Types (e.g., Electric Scooter, RV)

  • Create a new subclass of Vehicle.
  • Add corresponding VehicleType enum value.
  • Create a new ParkingSpot subclass if needed, or map to existing spot types.
  • Update canFitVehicle() mapping - no changes to core allocation logic.

Adding New Payment Methods (e.g., Wallet, Crypto)

  • Implement a new PaymentProcessor class conforming to the payment interface.
  • Register it in the payment factory.
  • No changes to Ticket, ExitPanel, or fee calculation.

Dynamic Pricing

  • Implement a new ParkingRate strategy (e.g., SurgePricingRate).
  • Factor in: time of day, current occupancy percentage, day of week, special events.
  • Swap strategy at runtime based on conditions.

Reservation System

  • Add a Reservation entity with: vehicleInfo, spotType, startTime, endTime.
  • Introduce RESERVED state in ParkingSpot.
  • Reserved spots are excluded from general allocation.
  • Add a ReservationService that manages booking, cancellation, and no-show handling.

Valet Parking

  • Add a ValetService class that wraps the standard park/unpark flow.
  • Valet has a queue of pending park/retrieve requests.
  • Vehicle owner receives a valet token instead of a standard ticket.
  • Valet staff use an internal interface to assign/retrieve vehicles.
  • Priority queue for retrieval requests (VIP customers get faster service).

Multi-Lot Management

  • Abstract the ParkingLot behind a ParkingFacility interface.
  • Create a ParkingLotNetwork that aggregates multiple facilities.
  • Add a routing layer that directs vehicles to the nearest lot with availability.
  • Centralized reporting and analytics across all lots.

License Plate Recognition (LPR)

  • Add a CameraService at entry/exit that captures plate images.
  • Integrate with an OCR service to extract plate numbers.
  • Enable ticketless entry/exit - the plate itself becomes the identifier.
  • Useful for lost ticket scenarios and monthly pass holders.

Interview Follow-ups

Q1: How would you handle concurrent access to the same spot?

Answer: Use atomic Compare-and-Swap (CAS) at the spot level. When assignVehicle() is called, it atomically checks if the spot status is FREE and sets it to OCCUPIED. If another thread already changed it, the CAS fails and the caller retries with the next available spot. This provides per-spot lock granularity - far better than locking the entire floor or lot. In a database-backed system, use optimistic locking with a version column: UPDATE spots SET status='OCCUPIED', version=version+1 WHERE id=? AND status='FREE' AND version=?.

Q2: How would you add a reservation system?

Answer: Introduce a Reservation entity that holds the vehicle type, desired time window, and assigned spot. Add a RESERVED status to SpotStatus. The reservation service pre-allocates spots and transitions them to RESERVED state. When the vehicle arrives, the entry panel matches the license plate to the reservation and directs the vehicle to the reserved spot. Handle no-shows with a grace period (e.g., 15 minutes) after which the reservation is cancelled and the spot returns to FREE. Reservations should be stored with expiry timestamps and cleaned up by a background job.

Q3: How would you scale this system for a chain of parking lots?

Answer: Extract the Singleton into a multi-tenant service. Each physical lot gets a unique lotId. The central service manages lot metadata, while each lot runs its own allocation logic independently. Use an API gateway to route requests to the correct lot instance. For cross-lot features (find nearest lot with availability), add a lightweight aggregation service that polls each lot's availability count.

Follow-up 4: How would you implement dynamic pricing based on demand?

Hints:

  • Think about what inputs drive price changes (occupancy %, time of day, events nearby).
  • Consider how the Strategy pattern lets you swap rate calculators at runtime without touching the core flow.

Follow-up 5: How would you handle a multi-level lot where certain floors are temporarily closed for maintenance?

Hints:

  • Consider an OUT_OF_SERVICE state for an entire floor, not just individual spots.
  • Think about how the allocation algorithm skips unavailable floors and how the display board reflects this.

Counter Questions to Ask Interviewer

Before diving into design, clarify scope and constraints:

  1. Scale: "How many vehicles per day are we expecting? Is this a mall lot (hundreds) or an airport lot (thousands)?"
  2. Vehicle types: "Do we need to support oversized vehicles like buses or RVs, or just motorcycles and cars?"
  3. Payment complexity: "Is payment handled at exit only, or do we need pay-on-foot stations within the lot? Do we need to support monthly passes or subscriptions?"
  4. Reservation: "Should the system support advance reservations, or is it first-come-first-served only?"
  5. Accessibility: "Are there specific requirements for handicapped spots, EV charging, or premium/VIP spots?"
  6. Persistence: "Should this be an in-memory design, or do you want me to consider database schema and persistence?"
  7. Distributed: "Is this a single-lot system or should the design support a chain of lots under one management system?"

These questions demonstrate that you think about requirements before jumping into code - a key signal interviewers look for.

Pro tip: After asking these questions, summarize the scope back to the interviewer: "So we're designing a single multi-floor lot, first-come-first-served, with hourly billing and cash/card payment at exit. I'll focus on the core park/unpark flow and spot allocation, then extend to display boards and concurrency." This shows structured thinking and sets clear boundaries for your design.


References


This problem is a staple of LLD interviews at companies like Google, Amazon, Microsoft, and Uber. Master the core design, practice explaining trade-offs verbally, and be ready to extend it in any direction the interviewer takes you.