issue 117apr 27mmxxvi
est. 2026
delhi/ncr edition
vol. IX · no. 117
PapersAdda
placement intelligence, source-anchored
1,000+ briefs · 24 campuses · 120+ companies
verified offers · sourced from r/developersIndia
razorpay₹65.00 LPA· iit-d · sde-1google₹54.00 LPA· iiit-h · swe-imicrosoft₹49.50 LPA· iit-b · sdeatlassian₹38.00 LPA· nit-w · sde-1amazon₹44.20 LPA· bits-p · sde-1uber₹42.00 LPA· iit-kgp · sde-1razorpay₹65.00 LPA· iit-d · sde-1google₹54.00 LPA· iiit-h · swe-imicrosoft₹49.50 LPA· iit-b · sdeatlassian₹38.00 LPA· nit-w · sde-1amazon₹44.20 LPA· bits-p · sde-1uber₹42.00 LPA· iit-kgp · sde-1
section: Topics & Practice / brief
08 Jun 2026
placement brief / Topics & Practice / brief / 08 Jun 2026

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

Candidates report parking lot as the most frequently asked LLD problem, appearing in roughly 25-30% of LLD rounds at Amazon,...

on this page§ 14
advertisement

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 PrincipleHow this design satisfies it
Single ResponsibilityParkingLot manages entry/exit; PricingStrategy handles fees; DisplayBoard handles UI
Open/ClosedNew vehicle or spot type: add a class. New pricing: implement PricingStrategy. No existing classes modified.
Liskov SubstitutionCar, Truck, Motorcycle all safely substitutable for Vehicle
Interface SegregationPricingStrategy has only one method: calculate_fee
Dependency InversionParkingLot 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.


FAQs

What are the key classes in a parking lot design?

Core classes: ParkingLot (singleton, manages floors), Floor (contains spots), ParkingSpot (base class with Compact/Large/Handicapped/Motorcycle subtypes), Vehicle (base class with Car/Truck/Motorcycle subtypes), Ticket (issued on entry), PricingStrategy (interface), DisplayBoard (shows available spots). The Ticket links Vehicle to Spot for the duration of the visit.

How do you handle concurrency in a parking lot system?

Use a ReentrantLock or threading.Lock per floor (or per spot cluster) rather than a global lock. A global lock is a bottleneck. Floor-level locking allows concurrent entry/exit on different floors. The find_available_spot method must be atomic with the spot assignment to prevent two vehicles being assigned the same spot.

How do you calculate parking fees in an extensible way?

Define a PricingStrategy interface with a calculate_fee(duration, vehicle_type) method. Implement concrete strategies: FlatRatePricing, HourlyPricing, DayRatePricing, SurgePricing. Inject the strategy into ParkingLot at construction time. Adding new pricing models requires only a new class, not modifying ParkingLot.

advertisement
Sources and review notesreviewed 8 Jun 2026
Article-specific sources
Verification window
Page last edited 8 Jun 2026 by Aditya Sharma. A review date records an editorial edit, not a guarantee that every external fact is still current.
Evidence labels

Official notices, candidate reports, offer documents, and editorial practice questions carry different confidence levels. The visible source list lets you inspect the evidence instead of relying on a blanket verification badge.

Verification policy: /editorial-standards/. Found something incorrect? Submit a correction - we respond within 48 hours.

topic cluster

More resources in Topics & Practice

Use the category hub to browse similar questions, exam patterns, salary guides, and preparation resources related to this topic.

Open Topics & Practice hubBrowse all articles

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 →
related guides
more from PapersAdda
Exam PatternsMicrosoft Interview Pattern Bank 2026: LRU Cache, OneDrive & AA Round
13 min read
Interview QuestionsMicrosoft New Grad 4-Round Interview 2026: The Real Loop With Every Question Asked
9 min read
Company Placement PapersStripe Placement Papers 2026 with Solutions
12 min read
Company Placement PapersAccenture Game-Based Cognitive 2026, the New Pattern Decoded
9 min read

Share this guide