System Design: Parking Lot LLD 2026 [Complete OOD with Code]

What changed in 2026 drives
Mass-recruiter offer letters are flatter for 2026 batch - the 4-5 LPA ASE band has barely budged in three years while inflation eats real wages. Premium tracks (Digital, Pro, Elite, Specialist) are still where the differential lives, and they are entirely test-driven. If you are aiming higher than the default offer, the coding round is not optional pageantry - it is the entire interview.
What I'd actually study for this
- 01Two solid coding-round answers (1 medium-hard DSA each, with edge-case discussion) > five half-baked ones
- 02One real project you can defend end-to-end - file paths, design decisions, and what you would change
- 03One DBMS schema you actually built (not a textbook ER diagram), with at least 3 join-heavy queries written from memory
- 04Three behavioural STAR stories: failure recovered, conflict handled, ownership taken
Where most candidates trip up
The single biggest mistake is treating company-specific guides as primary prep and DSA as secondary. It is the opposite. Mass recruiters use the test as a filter, but premium tracks at every IT services company use coding to allocate offer band. Spend 70% of prep time on DSA + system fundamentals, 20% on company-specific patterns, 10% on HR rehearsal. Reverse that ratio and you collect the default offer.
Editorial commentary by Aditya Sharma · written for PapersAdda · not generated, not aggregated.
Last Updated: June 2026
Why Parking Lot is the Most Common LLD Problem
Candidates report parking lot as the most frequently asked LLD problem, appearing in roughly 25-30% of LLD rounds at Amazon, Flipkart, Paytm, and Goldman Sachs. Based on public preparation resources and candidate-reported interview threads, it tests class hierarchy design, SOLID adherence, concurrency awareness, and design pattern recognition in a single question.
Requirements Gathering
Functional requirements:
- Multiple floors, multiple spots per floor
- Spot types: Motorcycle, Compact, Large, Handicapped
- Vehicle types: Motorcycle, Car, SUV/Truck
- Issue ticket on vehicle entry (spot assignment)
- Accept payment on exit, compute fee based on duration
- Display real-time availability per floor
Non-functional requirements:
- Thread-safe: multiple entry/exit gates running concurrently
- Extensible: new spot types, vehicle types, pricing models without major refactoring
- Consistency: no two vehicles assigned the same spot
Scale and Capacity Assumptions
Before designing classes, state your scale assumptions out loud in the interview. They shape your concurrency and storage choices.
Single large lot:
Floors: 6
Spots per floor: 400
Total spots: 2,400
Entry/exit gates: 8 (4 entry, 4 exit) operating concurrently
Peak arrival rate: 1 vehicle per gate per 10 seconds = 0.4 vehicles/sec
Average parking duration: 90 minutes
Daily throughput: roughly 6,000 to 8,000 vehicles
Ticket record size:
ticket_id (UUID): 16 bytes
vehicle plate: 12 bytes
spot_id: 8 bytes
entry/exit timestamps: 16 bytes
fee: 8 bytes
Total: ~60 bytes per ticket
Daily ticket storage: 8,000 * 60 = ~480 KB/day
Yearly: ~175 MB (trivial; a single relational DB table suffices)
The takeaway: a single parking lot is a low-throughput system. The hard part is not storage or QPS, it is correctness under concurrency (no double-assigned spots) and extensibility (new vehicle types, new pricing). This is why the problem is graded as LLD (object design) rather than HLD (distributed systems). If the interviewer scales it to a nationwide chain of 10,000 lots, the storage and a central availability service become relevant, which is covered in the follow-up section.
Class Hierarchy Design
ParkingLot (Singleton)
|-- List<Floor>
|-- PricingStrategy (injected)
|-- DisplayBoard
Floor
|-- List<ParkingSpot>
|-- ReentrantLock (thread safety)
ParkingSpot (abstract)
|-- MotorcycleSpot
|-- CompactSpot
|-- LargeSpot
|-- HandicappedSpot
Vehicle (abstract)
|-- Motorcycle
|-- Car
|-- Truck
Ticket
|-- vehicle, spot, entry_time, exit_time
PricingStrategy (interface)
|-- FlatRatePricing
|-- HourlyPricing
PaymentProcessor
|-- Cash, Card, UPI
DisplayBoard
|-- shows_available_count per floor
Full Implementation
from enum import Enum
from datetime import datetime
import threading
import uuid
from abc import ABC, abstractmethod
class SpotType(Enum):
MOTORCYCLE = 1
COMPACT = 2
LARGE = 3
HANDICAPPED = 4
class VehicleType(Enum):
MOTORCYCLE = 1
CAR = 2
TRUCK = 3
class Vehicle(ABC):
def __init__(self, license_plate, vehicle_type):
self.license_plate = license_plate
self.vehicle_type = vehicle_type
@property
@abstractmethod
def compatible_spots(self):
pass
class Motorcycle(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, VehicleType.MOTORCYCLE)
@property
def compatible_spots(self):
return [SpotType.MOTORCYCLE, SpotType.COMPACT]
class Car(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, VehicleType.CAR)
@property
def compatible_spots(self):
return [SpotType.COMPACT, SpotType.LARGE]
class Truck(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, VehicleType.TRUCK)
@property
def compatible_spots(self):
return [SpotType.LARGE]
class ParkingSpot:
def __init__(self, spot_id, spot_type, floor_number):
self.spot_id = spot_id
self.spot_type = spot_type
self.floor_number = floor_number
self.is_available = True
self.vehicle = None
def assign(self, vehicle):
"""Assign vehicle to this spot."""
if not self.is_available:
raise Exception(f"Spot {self.spot_id} is already occupied")
self.vehicle = vehicle
self.is_available = False
def vacate(self):
"""Remove vehicle from spot."""
self.vehicle = None
self.is_available = True
def can_fit(self, vehicle):
return self.is_available and self.spot_type in vehicle.compatible_spots
class PricingStrategy(ABC):
@abstractmethod
def calculate_fee(self, duration_minutes, vehicle_type):
pass
class HourlyPricing(PricingStrategy):
RATES = {
VehicleType.MOTORCYCLE: 10,
VehicleType.CAR: 30,
VehicleType.TRUCK: 60
}
def calculate_fee(self, duration_minutes, vehicle_type):
hours = max(1, (duration_minutes + 59) // 60) # ceil to nearest hour
return hours * self.RATES.get(vehicle_type, 30)
class FlatRatePricing(PricingStrategy):
RATES = {
VehicleType.MOTORCYCLE: 50,
VehicleType.CAR: 100,
VehicleType.TRUCK: 200
}
def calculate_fee(self, duration_minutes, vehicle_type):
return self.RATES.get(vehicle_type, 100)
class Ticket:
def __init__(self, vehicle, spot):
self.ticket_id = str(uuid.uuid4())[:8]
self.vehicle = vehicle
self.spot = spot
self.entry_time = datetime.now()
self.exit_time = None
self.fee = 0
@property
def duration_minutes(self):
end = self.exit_time or datetime.now()
return int((end - self.entry_time).total_seconds() / 60)
class Floor:
def __init__(self, floor_number, spots):
self.floor_number = floor_number
self.spots = spots
self._lock = threading.Lock()
def available_count(self):
return sum(1 for s in self.spots if s.is_available)
def find_and_assign_spot(self, vehicle):
"""Atomic: find available spot + assign. Returns spot or None."""
with self._lock:
for spot in self.spots:
if spot.can_fit(vehicle):
spot.assign(vehicle)
return spot
return None
class DisplayBoard:
def __init__(self, floors):
self.floors = floors
def show(self):
for floor in self.floors:
print(f"Floor {floor.floor_number}: {floor.available_count()} spots available")
class ParkingLot:
"""
Singleton. Manages multiple floors and issues/validates tickets.
"""
_instance = None
_init_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if not cls._instance:
with cls._init_lock:
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, pricing_strategy=None):
if hasattr(self, '_initialized'):
return
self.floors = []
self.active_tickets = {} # ticket_id -> Ticket
self.pricing = pricing_strategy or HourlyPricing()
self.display_board = None
self._initialized = True
def add_floor(self, floor):
self.floors.append(floor)
self.display_board = DisplayBoard(self.floors)
def park_vehicle(self, vehicle):
"""Entry: find spot, issue ticket."""
for floor in self.floors:
spot = floor.find_and_assign_spot(vehicle)
if spot:
ticket = Ticket(vehicle, spot)
self.active_tickets[ticket.ticket_id] = ticket
print(f"Parked {vehicle.license_plate} at floor {spot.floor_number}, spot {spot.spot_id}")
print(f"Ticket ID: {ticket.ticket_id}")
return ticket
print("Parking lot is full")
return None
def exit_vehicle(self, ticket_id):
"""Exit: validate ticket, compute fee, free spot."""
ticket = self.active_tickets.get(ticket_id)
if not ticket:
raise Exception(f"Invalid ticket: {ticket_id}")
ticket.exit_time = datetime.now()
ticket.fee = self.pricing.calculate_fee(
ticket.duration_minutes,
ticket.vehicle.vehicle_type
)
ticket.spot.vacate()
del self.active_tickets[ticket_id]
print(f"Vehicle {ticket.vehicle.license_plate} exited. Fee: Rs. {ticket.fee}")
return ticket.fee
Usage Example
# Create parking lot with 2 floors
lot = ParkingLot(pricing_strategy=HourlyPricing())
floor1_spots = [
ParkingSpot('F1-M1', SpotType.MOTORCYCLE, 1),
ParkingSpot('F1-M2', SpotType.MOTORCYCLE, 1),
ParkingSpot('F1-C1', SpotType.COMPACT, 1),
ParkingSpot('F1-C2', SpotType.COMPACT, 1),
ParkingSpot('F1-L1', SpotType.LARGE, 1),
]
lot.add_floor(Floor(1, floor1_spots))
# Park vehicles
car = Car("KA01AB1234")
truck = Truck("MH12CD5678")
bike = Motorcycle("DL3EF9012")
t1 = lot.park_vehicle(car)
t2 = lot.park_vehicle(truck)
t3 = lot.park_vehicle(bike)
lot.display_board.show()
# Exit
import time
time.sleep(1)
lot.exit_vehicle(t1.ticket_id)
SOLID Adherence Analysis
| SOLID Principle | How this design satisfies it |
|---|---|
| Single Responsibility | ParkingLot manages entry/exit; PricingStrategy handles fees; DisplayBoard handles UI |
| Open/Closed | New vehicle or spot type: add a class. New pricing: implement PricingStrategy. No existing classes modified. |
| Liskov Substitution | Car, Truck, Motorcycle all safely substitutable for Vehicle |
| Interface Segregation | PricingStrategy has only one method: calculate_fee |
| Dependency Inversion | ParkingLot depends on PricingStrategy (abstraction), not HourlyPricing (concrete) |
Concurrency Design
The floor-level lock ensures that finding and assigning a spot is atomic. Without this, two concurrent entry requests could both see the same spot as available and assign it to two vehicles. The lock is per-floor rather than per-lot: vehicles entering on floor 1 do not block vehicles entering on floor 2.
For a multi-gate entry system at very high scale, a spot reservation queue (add spot_id to a Redis set, pop to claim) replaces the in-memory lock, enabling distributed concurrency across multiple gate processes. The Redis SPOP operation is atomic, so two gate processes can never claim the same spot id even when they execute simultaneously, which is exactly the guarantee the in-memory floor lock provides within a single process. This is the standard upgrade path interviewers want to hear when they push the design from one process to many.
Payment Processing Design
Payment is the second extensibility axis interviewers probe. The wrong approach is a giant if-else on payment method inside the exit flow. The right approach is the Strategy pattern again, mirroring pricing.
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
"""Return a PaymentResult (success/failure + transaction id)."""
pass
class CashPayment(PaymentMethod):
def pay(self, amount):
return PaymentResult(success=True, txn_id=str(uuid.uuid4()), method="CASH")
class CardPayment(PaymentMethod):
def __init__(self, gateway):
self.gateway = gateway # injected payment gateway client
def pay(self, amount):
resp = self.gateway.charge(amount)
return PaymentResult(success=resp.ok, txn_id=resp.id, method="CARD")
class UpiPayment(PaymentMethod):
def __init__(self, upi_client):
self.upi_client = upi_client
def pay(self, amount):
resp = self.upi_client.collect(amount)
return PaymentResult(success=resp.ok, txn_id=resp.ref, method="UPI")
The exit flow accepts an injected PaymentMethod and never branches on type. Adding a new method (wallet, NFC tag) is a new class, not an edit to existing code. This is the Open/Closed Principle applied a second time, and calling that out explicitly scores points.
For card and UPI, the gateway call can fail or time out. The exit flow must handle a failed payment by keeping the spot occupied and the ticket active, then surfacing a retry prompt. Never free the spot before payment confirmation, otherwise a vehicle could leave without paying and the spot would be marked free prematurely.
State Transitions for a Parking Spot
Modeling the spot as an explicit state machine prevents a class of bugs. A spot moves through AVAILABLE, then RESERVED (optional), then OCCUPIED, then back to AVAILABLE on vacate. Illegal transitions (OCCUPIED directly to RESERVED, or vacating an already-AVAILABLE spot) should raise rather than silently corrupt state.
AVAILABLE --reserve--> RESERVED --assign--> OCCUPIED
AVAILABLE --assign----> OCCUPIED
RESERVED --expire(15min)--> AVAILABLE
OCCUPIED --vacate-----> AVAILABLE
Encoding this as a guarded method set (reserve, assign, vacate, expire) keeps the invariant "a spot is in exactly one state" enforced in one place. When the interviewer asks "what if two threads call assign on the same spot," the answer is: the floor lock serializes the find-and-assign, and the spot's assign method rejects a second call because the state is no longer AVAILABLE.
Follow-up Questions and Answers
How do you support reservation (pre-booking)? Add a ReservationService that holds a spot for a user for 15 minutes. Spot status gets a RESERVED state in addition to AVAILABLE and OCCUPIED. A background sweeper expires reservations past their TTL and returns spots to AVAILABLE. The reservation must be atomic with the spot lookup, otherwise two users could reserve the same spot.
How do you support multiple parking lot locations (chain)? Add a ParkingLotChain that holds multiple ParkingLot instances. The chain's find_spot() delegates to each lot. Each lot is still a singleton within its process. At nationwide scale (10,000 lots), the in-memory singleton breaks down: availability moves to a central service backed by Redis, where each lot publishes its live free-spot counts and the chain queries the cache rather than each lot directly.
How do you add a subscription model (monthly pass)? Add a MembershipStrategy implementing PricingStrategy that checks the vehicle's membership tier and applies a flat monthly rate or free parking logic. Membership lookup happens at exit time before fee calculation.
How do you handle a lost ticket? Charge a fixed maximum daily rate. The system looks up the active ticket by license plate (which requires an index on plate, not just ticket_id) and applies a lost-ticket penalty fee defined in the pricing strategy.
How do you find the nearest available spot to an entrance? Precompute the distance from each entrance to each spot. Maintain a min-heap of available spots keyed by distance per entrance. On assign, pop the nearest; on vacate, push back. This turns the find operation into O(log n) instead of a linear scan, which matters for a 2,400-spot lot at peak.
How do you make the DisplayBoard update in real time across gates? The DisplayBoard subscribes to spot state-change events (Observer pattern). Each assign/vacate publishes an event, and the board decrements or increments the per-floor available count. This avoids re-scanning all spots on every display refresh.
Spot Assignment Algorithm: Nearest Available vs Compact Best Fit
Two strategies for assigning spots to vehicles. Nearest available: scan spots in order, assign the first one that fits the vehicle type. Fast, O(1) with a queue per type. Compact best fit: assign the smallest spot that fits the vehicle (a motorcycle gets a motorcycle spot before a compact spot). This maximizes utilization when large spots are scarce. In most LLD interviews, nearest available is sufficient; mention best fit as an optimization if the interviewer asks about utilization.
Related Articles
Methodology applied to this articlelast verified 8 Jun 2026
- No fabricated salary numbers or success rates. If we quote a range, it's sourced.
- No noun-substituted templates. This article was not generated by swapping company names in a stock prompt.
- No paid placements, sponsored coaching links, or affiliate-shilled course pushes.
Explore this topic cluster
More resources in Uncategorized
Use the category hub to browse similar questions, exam patterns, salary guides, and preparation resources related to this topic.
Paid contributor programme
Sat this this year? Share your story, earn ₹500.
First-person experience reports help future candidates prep smarter. We pay verified contributors ₹500 via UPI per accepted story - with byline.
Submit your story →Ready to practice?
Take a free timed mock test
Put what you learned into practice. Our mock tests match the 2026 pattern with timer, navigator, reveal, and score breakdown. No signup.
Start Free Mock Test →More from PapersAdda
Microsoft Interview Pattern Bank 2026: LRU Cache, OneDrive & AA Round
Infosys HackWithInfy 2026: 3-Round Pattern + Past Winner Code
NVIDIA Coding Round Questions 2026: Patterns + Code
Salesforce Coding Round Questions 2026: Patterns + Code