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
- Park a vehicle - Assign an available parking spot to an incoming vehicle based on its type and size.
- Unpark a vehicle - Free the spot when the vehicle leaves and update availability.
- Find available spot - Locate the nearest available spot for a given vehicle type on any floor.
- Issue a ticket - Generate a parking ticket at entry with timestamp, assigned spot, and vehicle details.
- Calculate parking fee - Compute the fee based on duration, vehicle type, and rate structure.
- Process payment - Accept payment via cash, credit card, or UPI at exit panels.
- Support multiple vehicle types - Handle motorcycles, cars, buses, and trucks with different spot sizes.
- Multiple floors - Manage spots across multiple parking floors with independent tracking.
- Multiple entry/exit points - Support concurrent vehicle entry and exit at different gates.
- Display real-time availability - Show available spots per floor and per vehicle type on display boards.
Non-Functional Requirements
- Concurrency - Multiple vehicles entering/exiting simultaneously must not cause double-allocation of spots.
- Scalability - The design should support adding new floors, spots, or entry/exit points without major refactoring.
- Real-time tracking - Availability information must update within seconds of a park/unpark event.
- Fault tolerance - Ticket and payment data must persist across power outages or system restarts.
- Low latency - Spot allocation and fee calculation should complete in constant or logarithmic time.
Constraints & Assumptions
| Parameter | Value |
|---|---|
| Number of floors | 5 |
| Spots per floor | 200 (80 compact, 80 regular, 20 large, 10 motorcycle, 10 handicapped) |
| Total capacity | 1,000 spots |
| Vehicle types | Motorcycle, Car, Truck/Bus |
| Spot types | Motorcycle, Compact, Regular, Large, Handicapped, EV Charging |
| Entry points | 3 |
| Exit points | 3 |
| Payment methods | Cash, Credit Card, UPI |
| Rate structure | Hourly (first hour flat, then per-hour increments) |
| Operating hours | 24/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
| Entity | Responsibility |
|---|---|
| ParkingLot | Top-level singleton managing the entire system |
| ParkingFloor | Manages spots on a single floor |
| ParkingSpot | Abstract base - subtypes: MotorcycleSpot, CompactSpot, RegularSpot, LargeSpot, HandicappedSpot, EVSpot |
| Vehicle | Abstract base - subtypes: Motorcycle, Car, Truck, Bus |
| Ticket | Issued at entry; holds entry time, spot reference, vehicle info |
| Payment | Processes fee calculation and payment transaction |
| EntryPanel | Handles vehicle arrival, ticket issuance, spot assignment |
| ExitPanel | Handles ticket scanning, fee display, payment processing |
| DisplayBoard | Shows real-time availability per floor |
| ParkingRate | Encapsulates 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
| Strategy | Pros | Cons |
|---|---|---|
| Nearest to entry | Better UX, less driving time | Uneven wear on near-entry spots; complex to compute |
| Random | Even distribution, simple | Poor UX, longer search time |
| Floor-based (fill one floor at a time) | Simple tracking, efficient display | Lower 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
| Model | Use Case |
|---|---|
| Hourly | Standard commercial lots - charge per hour with first-hour minimum |
| Flat rate | Event parking, airport long-term |
| Tiered | Peak/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 UPDATEor 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
| Pattern | Application | Benefit |
|---|---|---|
| Singleton | ParkingLot | Single point of coordination; prevents multiple instances managing the same physical lot |
| Strategy | ParkingRate (HourlyRate, FlatRate, TieredRate) | Swap pricing algorithms without modifying client code |
| Factory Method | VehicleFactory, SpotFactory | Encapsulate creation logic; decouple client from concrete classes |
| Observer | DisplayBoard observes ParkingFloor | Real-time UI updates without tight coupling |
| State | ParkingSpot status transitions (FREE → OCCUPIED → RESERVED → OUT_OF_SERVICE) | Clean state machine; prevents invalid transitions |
| Template Method | Payment processing (validate → calculate → charge → confirm) | Enforce processing steps while allowing method-specific variation |
| Repository | TicketRepository, SpotRepository | Abstract 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:
- Vehicle arrives at EntryPanel → Factory creates the Vehicle object from input.
- Singleton ParkingLot is called to find a spot → internally iterates floors.
- State pattern on ParkingSpot validates the FREE → OCCUPIED transition atomically.
- Observer pattern triggers DisplayBoard update after spot status changes.
- At exit, Strategy pattern selects the correct ParkingRate implementation.
- 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
VehicleTypeenum value. - Create a new
ParkingSpotsubclass 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
PaymentProcessorclass conforming to the payment interface. - Register it in the payment factory.
- No changes to Ticket, ExitPanel, or fee calculation.
Dynamic Pricing
- Implement a new
ParkingRatestrategy (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
Reservationentity with: vehicleInfo, spotType, startTime, endTime. - Introduce RESERVED state in ParkingSpot.
- Reserved spots are excluded from general allocation.
- Add a
ReservationServicethat manages booking, cancellation, and no-show handling.
Valet Parking
- Add a
ValetServiceclass 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
ParkingFacilityinterface. - Create a
ParkingLotNetworkthat 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
CameraServiceat 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:
- Scale: "How many vehicles per day are we expecting? Is this a mall lot (hundreds) or an airport lot (thousands)?"
- Vehicle types: "Do we need to support oversized vehicles like buses or RVs, or just motorcycles and cars?"
- 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?"
- Reservation: "Should the system support advance reservations, or is it first-come-first-served only?"
- Accessibility: "Are there specific requirements for handicapped spots, EV charging, or premium/VIP spots?"
- Persistence: "Should this be an in-memory design, or do you want me to consider database schema and persistence?"
- 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
- Strategy Pattern - Used for interchangeable pricing algorithms
- Factory Pattern - Used for vehicle and spot creation
- Singleton Pattern - Used for the ParkingLot instance
- Observer Pattern - Used for display board updates
- State Pattern - Used for spot status transitions
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.