LLD Interview Framework & Walkthrough
A repeatable 5-step framework for LLD interviews. Includes a complete walkthrough of the Parking Lot problem — the most commonly asked LLD problem — with class diagrams and code.
The 5-Step LLD Framework
Clarify Scope (3-5 min)
Ask: What actors use the system? What are the core use cases? Any constraints (concurrency, scale)? For Parking Lot: 'Is this one lot or a network? What vehicle types? How is pricing structured?'
Extract Entities (5 min)
Read the requirements. Every noun is a potential class. Every verb is a potential method. For Parking Lot: ParkingLot, Floor, ParkingSpot, Vehicle, Ticket, PricingStrategy.
Design Class Structure (10-15 min)
Define classes, their attributes, and relationships. Use inheritance for is-a (CompactSpot is-a ParkingSpot). Use composition for has-a (ParkingLot has Floors). Define interfaces for extensibility points.
Implement Key Methods (15-20 min)
Focus on the most interesting 2-3 methods that demonstrate design thinking. For Parking Lot: park(), leave(), findAvailableSpot(). Show error handling and concurrency awareness.
Discuss Extensions (5 min)
Proactively raise: 'To add a new vehicle type, we'd just add a new Vehicle subclass — no changes to existing code.' This demonstrates Open/Closed awareness.
LLD Interview Framework — Design Process
Parking Lot — Class Diagram with SOLID + Patterns Applied
Parking Lot — Core Implementation
from abc import ABC, abstractmethod
from datetime import datetime
from enum import Enum
import threading
import uuid
class VehicleType(Enum):
MOTORCYCLE = "motorcycle"
CAR = "car"
TRUCK = "truck"
class SpotType(Enum):
COMPACT = "compact" # motorcycles
STANDARD = "standard" # cars
LARGE = "large" # trucks
class Vehicle(ABC):
def __init__(self, license_plate: str):
self.license_plate = license_plate
@property
@abstractmethod
def type(self) -> VehicleType: pass
@property
@abstractmethod
def required_spot(self) -> SpotType: pass
class Car(Vehicle):
@property
def type(self): return VehicleType.CAR
@property
def required_spot(self): return SpotType.STANDARD
class Motorcycle(Vehicle):
@property
def type(self): return VehicleType.MOTORCYCLE
@property
def required_spot(self): return SpotType.COMPACT
class ParkingSpot(ABC):
def __init__(self, spot_id: str, spot_type: SpotType):
self.spot_id = spot_id
self.spot_type = spot_type
self._vehicle: Vehicle | None = None
self._lock = threading.Lock()
@abstractmethod
def can_fit(self, vehicle: Vehicle) -> bool: pass
def park(self, vehicle: Vehicle) -> bool:
with self._lock:
if self._vehicle or not self.can_fit(vehicle):
return False
self._vehicle = vehicle
return True
def leave(self) -> Vehicle | None:
with self._lock:
v = self._vehicle
self._vehicle = None
return v
@property
def is_available(self) -> bool:
return self._vehicle is None
class StandardSpot(ParkingSpot):
def can_fit(self, vehicle): return vehicle.required_spot == SpotType.STANDARD
class CompactSpot(ParkingSpot):
def can_fit(self, vehicle): return vehicle.required_spot in (SpotType.COMPACT, SpotType.STANDARD)
class PricingStrategy(ABC):
@abstractmethod
def calculate_fee(self, ticket: "Ticket") -> float: pass
class HourlyPricing(PricingStrategy):
RATES = {VehicleType.MOTORCYCLE: 1.0, VehicleType.CAR: 2.5, VehicleType.TRUCK: 4.0}
def calculate_fee(self, ticket):
hours = max(1, (ticket.exit_time - ticket.entry_time).seconds / 3600)
return round(hours * self.RATES.get(ticket.vehicle.type, 2.5), 2)
class Ticket:
def __init__(self, vehicle: Vehicle, spot: ParkingSpot):
self.id = str(uuid.uuid4())[:8]
self.vehicle = vehicle
self.spot = spot
self.entry_time = datetime.now()
self.exit_time: datetime | None = None
class ParkingLot:
_instance = None # Singleton
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, spots: list[ParkingSpot], pricing: PricingStrategy):
if hasattr(self, '_initialized'):
return
self.spots = spots
self.pricing = pricing
self.active_tickets: dict[str, Ticket] = {}
self._initialized = True
def park(self, vehicle: Vehicle) -> Ticket | None:
for spot in self.spots:
if spot.is_available and spot.can_fit(vehicle):
if spot.park(vehicle):
ticket = Ticket(vehicle, spot)
self.active_tickets[ticket.id] = ticket
return ticket
return None # full
def leave(self, ticket_id: str) -> float:
ticket = self.active_tickets.pop(ticket_id, None)
if not ticket:
raise ValueError(f"Ticket {ticket_id} not found")
ticket.exit_time = datetime.now()
ticket.spot.leave()
return self.pricing.calculate_fee(ticket)
Concurrency Note
Always mention thread safety for LLD problems in interviews. Parking lots will have concurrent park() calls. The lock in ParkingSpot.park() ensures two threads can't park in the same spot simultaneously. At senior level, discuss optimistic locking vs pessimistic locking for different scenarios.
Interview Questions
Click to reveal answersSign in to take the Quiz
This topic has 16 quiz questions with instant feedback and detailed explanations. Sign in to unlock quizzes.
Sign in to take quiz →