Phantom CodePhantom Code
Earn with UsBlogsHelp Center
Earn with UsBlogsMy WorkspaceFeedbackPricingHelp Center
Home/Blog/Low-Level Design (LLD) Questions: Parking Lot, Elevator, and More
By PhantomCode Team·Published April 30, 2026·3 min read
TL;DR

Low-Level Design interviews test object-oriented thinking, SOLID principles, and the ability to translate vague requirements into clean, extensible code. The four canonical problems — parking lot, elevator, vending machine, and library system — repeatedly appear at Google, Amazon, Meta, and Apple senior rounds. Master these patterns: enum-based state, separation of concerns across Vehicle/Spot/Level classes, strategy and factory patterns, and SOLID compliance, and you can handle any LLD prompt.

Low-Level Design (LLD) questions test your ability to translate real-world requirements into clean, extensible code. Unlike system design (which focuses on databases, caching, and scalability), LLD focuses on object-oriented design, design patterns, and good architecture. LLD questions appear in senior engineering interviews at Google, Amazon, Meta, and Apple. If you can write clean LLD code, you stand out. This guide walks you through the most common LLD problems with complete solutions.

LLD vs. System Design

System Design (HLD): How would you design YouTube? Focus on databases, scalability, distribution.

Low-Level Design (LLD): How would you design a parking lot system? Focus on classes, methods, design patterns, and clean code.

LLD questions are about writing code that's extensible, maintainable, and follows SOLID principles.

Key Design Patterns for LLD

Before diving into specific problems, understand these patterns:

Strategy Pattern: Different algorithms implementing the same interface.

Factory Pattern: Create objects without specifying exact classes.

Observer Pattern: Objects notify others about state changes.

Singleton Pattern: Exactly one instance of a class.

Adapter Pattern: Make incompatible interfaces work together.

Decorator Pattern: Add functionality to objects dynamically.

Problem 1: Design a Parking Lot

Requirements:

  • Parking lot has multiple levels, each with multiple spots
  • Spots have different sizes: compact, regular, large
  • Vehicles have different sizes: motorcycle, car, truck
  • Users can park, unpark, and check available spots
  • System tracks occupied and available spots

Design:

from enum import Enum
from datetime import datetime
from typing import Optional
 
class VehicleSize(Enum):
    MOTORCYCLE = 1
    CAR = 2
    TRUCK = 3
 
class ParkingSpotSize(Enum):
    COMPACT = 1
    REGULAR = 2
    LARGE = 3
 
class Vehicle:
    def __init__(self, license_plate: str, size: VehicleSize):
        self.license_plate = license_plate
        self.size = size
 
class ParkingSpot:
    def __init__(self, level: int, spot_number: int, size: ParkingSpotSize):
        self.level = level
        self.spot_number = spot_number
        self.size = size
        self.is_available = True
        self.vehicle: Optional[Vehicle] = None
 
    def park(self, vehicle: Vehicle) -> bool:
        if not self.is_available or not self._can_fit_vehicle(vehicle):
            return False
        self.is_available = False
        self.vehicle = vehicle
        return True
 
    def unpark(self) -> Vehicle:
        vehicle = self.vehicle
        self.is_available = True
        self.vehicle = None
        return vehicle
 
    def _can_fit_vehicle(self, vehicle: Vehicle) -> bool:
        if self.size == ParkingSpotSize.COMPACT:
            return vehicle.size == VehicleSize.MOTORCYCLE
        elif self.size == ParkingSpotSize.REGULAR:
            return vehicle.size in [VehicleSize.MOTORCYCLE, VehicleSize.CAR]
        else:  # LARGE
            return True
 
class ParkingLevel:
    def __init__(self, level: int, num_compact: int, num_regular: int, num_large: int):
        self.level = level
        self.spots = []
 
        spot_number = 0
        for _ in range(num_compact):
            self.spots.append(ParkingSpot(level, spot_number, ParkingSpotSize.COMPACT))
            spot_number += 1
        for _ in range(num_regular):
            self.spots.append(ParkingSpot(level, spot_number, ParkingSpotSize.REGULAR))
            spot_number += 1
        for _ in range(num_large):
            self.spots.append(ParkingSpot(level, spot_number, ParkingSpotSize.LARGE))
            spot_number += 1
 
    def park(self, vehicle: Vehicle) -> bool:
        for spot in self.spots:
            if spot.park(vehicle):
                return True
        return False
 
    def unpark(self, vehicle: Vehicle) -> bool:
        for spot in self.spots:
            if spot.vehicle == vehicle:
                spot.unpark()
                return True
        return False
 
    def available_spots_by_size(self, size: ParkingSpotSize) -> int:
        return sum(1 for spot in self.spots if spot.is_available and spot.size == size)
 
class ParkingLot:
    def __init__(self, num_levels: int, spots_per_level_config: list):
        self.levels = []
        for i in range(num_levels):
            config = spots_per_level_config[i]
            self.levels.append(ParkingLevel(i, config['compact'], config['regular'], config['large']))
 
    def park(self, vehicle: Vehicle) -> bool:
        for level in self.levels:
            if level.park(vehicle):
                return True
        return False
 
    def unpark(self, vehicle: Vehicle) -> bool:
        for level in self.levels:
            if level.unpark(vehicle):
                return True
        return False
 
    def get_available_spots(self) -> dict:
        spots = {'compact': 0, 'regular': 0, 'large': 0}
        for level in self.levels:
            spots['compact'] += level.available_spots_by_size(ParkingSpotSize.COMPACT)
            spots['regular'] += level.available_spots_by_size(ParkingSpotSize.REGULAR)
            spots['large'] += level.available_spots_by_size(ParkingSpotSize.LARGE)
        return spots

Key design decisions:

  • Vehicle and ParkingSpot are separate classes
  • ParkingLevel manages multiple spots
  • ParkingLot manages multiple levels
  • Encapsulation: each class knows only what it needs
  • Easy to extend: add new vehicle sizes or spot types

Problem 2: Design an Elevator System

Requirements:

  • Multiple elevators in a building
  • Elevators can move up/down
  • Users can request elevators from different floors
  • System should efficiently assign elevators

Design:

from enum import Enum
from collections import deque
 
class Direction(Enum):
    UP = 1
    DOWN = -1
    IDLE = 0
 
class Request:
    def __init__(self, floor: int):
        self.floor = floor
 
class Elevator:
    def __init__(self, elevator_id: int, max_floor: int):
        self.elevator_id = elevator_id
        self.current_floor = 0
        self.max_floor = max_floor
        self.direction = Direction.IDLE
        self.requests = deque()
 
    def add_request(self, request: Request):
        if 0 <= request.floor <= self.max_floor and request.floor != self.current_floor:
            self.requests.append(request)
            self._update_direction()
 
    def _update_direction(self):
        if not self.requests:
            self.direction = Direction.IDLE
            return
 
        next_floor = self.requests[0].floor
        if next_floor > self.current_floor:
            self.direction = Direction.UP
        elif next_floor < self.current_floor:
            self.direction = Direction.DOWN
 
    def move(self):
        if self.direction == Direction.IDLE:
            return
 
        self.current_floor += self.direction.value
 
        # Check if we've reached a requested floor
        if self.requests and self.requests[0].floor == self.current_floor:
            self.requests.popleft()
            self._update_direction()
 
    def is_moving(self) -> bool:
        return self.direction != Direction.IDLE
 
class ElevatorSystem:
    def __init__(self, num_elevators: int, max_floor: int):
        self.elevators = [Elevator(i, max_floor) for i in range(num_elevators)]
        self.max_floor = max_floor
 
    def request_elevator(self, floor: int):
        elevator = self._find_best_elevator(floor)
        if elevator:
            elevator.add_request(Request(floor))
 
    def _find_best_elevator(self, floor: int) -> Optional[Elevator]:
        # Closest idle elevator, or closest moving elevator
        idle_elevators = [e for e in self.elevators if not e.is_moving()]
 
        if idle_elevators:
            return min(idle_elevators, key=lambda e: abs(e.current_floor - floor))
 
        # All elevators moving; pick closest
        return min(self.elevators, key=lambda e: abs(e.current_floor - floor))
 
    def step(self):
        # Simulate time step
        for elevator in self.elevators:
            elevator.move()

Key design decisions:

  • Direction enum represents elevator state
  • Elevator maintains queue of requests
  • ElevatorSystem chooses best elevator for each request
  • Extensible: easy to add new assignment strategies

Problem 3: Design a Vending Machine

Requirements:

  • Users select items and insert money
  • System dispenses items if paid enough
  • System returns change
  • Track inventory

Design:

from enum import Enum
 
class Item:
    def __init__(self, name: str, price: float, quantity: int):
        self.name = name
        self.price = price
        self.quantity = quantity
 
class Coin(Enum):
    PENNY = 0.01
    NICKEL = 0.05
    DIME = 0.10
    QUARTER = 0.25
 
class VendingMachine:
    def __init__(self):
        self.inventory = {}
        self.money_inserted = 0
        self.coins = {}
 
    def add_item(self, location: str, item: Item, quantity: int):
        if location not in self.inventory:
            self.inventory[location] = item
        self.inventory[location].quantity += quantity
 
    def insert_coin(self, coin: Coin):
        self.money_inserted += coin.value
        self.coins[coin] = self.coins.get(coin, 0) + 1
 
    def select_item(self, location: str) -> str:
        if location not in self.inventory:
            return "Invalid location"
 
        item = self.inventory[location]
 
        if item.quantity == 0:
            return "Out of stock"
 
        if self.money_inserted < item.price:
            return f"Insufficient funds. Need ${item.price:.2f}, have ${self.money_inserted:.2f}"
 
        # Dispense item
        item.quantity -= 1
        change = self.money_inserted - item.price
        self.money_inserted = 0
 
        return f"Dispensed {item.name}. Change: ${change:.2f}"
 
    def return_money(self) -> float:
        change = self.money_inserted
        self.money_inserted = 0
        self.coins = {}
        return change

Problem 4: Design a Library Management System

Requirements:

  • Books can be borrowed and returned
  • Users have a borrowing limit
  • Track due dates and fines
  • Search for books by title or author

Design:

from datetime import datetime, timedelta
from typing import List
 
class User:
    def __init__(self, user_id: str, name: str):
        self.user_id = user_id
        self.name = name
        self.borrowed_books = []
        self.max_borrow_limit = 5
 
    def can_borrow(self) -> bool:
        return len(self.borrowed_books) < self.max_borrow_limit
 
    def borrow(self, book: 'Book') -> bool:
        if not self.can_borrow():
            return False
        self.borrowed_books.append(book)
        return True
 
    def return_book(self, book: 'Book') -> bool:
        if book in self.borrowed_books:
            self.borrowed_books.remove(book)
            return True
        return False
 
class Book:
    def __init__(self, isbn: str, title: str, author: str):
        self.isbn = isbn
        self.title = title
        self.author = author
        self.is_available = True
        self.borrowed_by: Optional[User] = None
        self.due_date: Optional[datetime] = None
 
    def borrow(self, user: User) -> bool:
        if not self.is_available:
            return False
        self.is_available = False
        self.borrowed_by = user
        self.due_date = datetime.now() + timedelta(days=14)
        return user.borrow(self)
 
    def return_book(self) -> float:
        fine = self._calculate_fine()
        self.is_available = True
        self.borrowed_by = None
        self.due_date = None
        return fine
 
    def _calculate_fine(self) -> float:
        if datetime.now() <= self.due_date:
            return 0.0
        days_late = (datetime.now() - self.due_date).days
        return days_late * 0.5  # 50 cents per day
 
class Library:
    def __init__(self):
        self.books = {}
        self.users = {}
 
    def add_book(self, book: Book):
        if book.isbn not in self.books:
            self.books[book.isbn] = []
        self.books[book.isbn].append(book)
 
    def search_by_title(self, title: str) -> List[Book]:
        return [b for books in self.books.values() for b in books if title.lower() in b.title.lower()]
 
    def search_by_author(self, author: str) -> List[Book]:
        return [b for books in self.books.values() for b in books if author.lower() in b.author.lower()]
 
    def borrow_book(self, user_id: str, isbn: str) -> bool:
        user = self.users.get(user_id)
        if not user or isbn not in self.books:
            return False
 
        for book in self.books[isbn]:
            if book.is_available and book.borrow(user):
                return True
 
        return False
 
    def return_book(self, user_id: str, isbn: str) -> float:
        user = self.users.get(user_id)
        if not user:
            return -1
 
        for book in user.borrowed_books:
            if book.isbn == isbn:
                user.return_book(book)
                fine = book.return_book()
                return fine
 
        return -1

Common LLD Mistakes

Mistake 1: Poor encapsulation Exposing internal details; let classes manage their own state.

Mistake 2: God objects One class doing too much. Distribute responsibilities.

Mistake 3: Ignoring SOLID principles S: Single responsibility, O: Open/closed, L: Liskov substitution, I: Interface segregation, D: Dependency inversion.

Mistake 4: No extensibility Hard-code behavior instead of using patterns that allow easy extension.

Mistake 5: Mixing concerns Don't mix business logic with I/O or persistence.

LLD Interview Tips

  1. Clarify requirements. Ask about edge cases and constraints.
  2. Design before coding. Sketch classes and relationships on paper/whiteboard.
  3. Use design patterns. They show you understand good design.
  4. Write clean code. Use proper naming, methods, and encapsulation.
  5. Think extensibility. "What if we add a new requirement?"
  6. Handle edge cases. Empty lists, null values, invalid inputs.
  7. Test your design. Walk through scenarios mentally.

Practicing LLD Effectively

  1. Solve 15-20 LLD problems
  2. For each, design before coding
  3. Code cleanly with proper OOP
  4. Consider scalability and extensibility
  5. Refactor after solving

Preparing for LLD Interviews

LLD interviews test whether you can write production-quality code. When you're practicing LLD design and want feedback on your object-oriented architecture, Phantom Code can help by providing design suggestions and catching architectural issues in real-time through its chat interface. While it's primarily designed for algorithmic interviews, the underlying technology can help with code quality feedback. Plans start at ₹499/month at phantomcode.co.

Final Thoughts

Low-Level Design is about writing code that's clean, extensible, and follows best practices. Master the problems in this guide and you'll be ready for LLD interviews at top tech companies.

Frequently Asked Questions

What's the difference between LLD and HLD (system design)?
HLD focuses on databases, caching, sharding, and distributed-system tradeoffs at scale. LLD focuses on classes, methods, design patterns, and SOLID principles within a single service or application.
Which design patterns show up most in LLD interviews?
Strategy (swappable algorithms), Factory (object creation), Observer (state-change notifications), Singleton (single instance), and Decorator (dynamic behavior). Knowing when to apply each is more important than memorizing the GoF catalog.
How long should you spend designing before coding in an LLD round?
Spend 5-10 minutes clarifying requirements and sketching classes and relationships. Don't write a single line of code until your interviewer agrees the class diagram is reasonable — it's much harder to refactor 30 lines of bad Python under time pressure.
What's the most common LLD interview mistake?
God objects — one class doing everything. Distribute responsibility across small, focused classes (Vehicle, ParkingSpot, ParkingLevel, ParkingLot) so each one is easy to reason about and extend.
How many LLD problems should you practice before a senior interview?
Solve 15-20 LLD problems end to end. After about a dozen, you'll start seeing the same building blocks (enums for state, registry patterns, allocation strategies) and pattern recognition kicks in.

Ready to Ace Your Next Interview?

Phantom Code provides real-time AI assistance during technical interviews. Solve DSA problems, system design questions, and more with instant AI-generated solutions.

Get Started

Related Articles

10 Things Great Candidates Do Differently in Technical Interviews

Ten behaviors that separate offer-winning candidates from average ones, from clarifying questions to optimizing without being asked.

From 5 Rejections to a Google Offer: One Engineer's Story

How a mid-level engineer turned five Google rejections into an L5 offer by fixing communication, system design depth, and exceptional reasoning.

Advanced SQL Interview Questions for Senior Engineers (2026)

Basic SQL gets you through L3. Senior roles require window functions, CTEs, execution plans, and real optimization know-how. Here is the complete advanced playbook.

Salary Guide|Resume Templates|LeetCode Solutions|FAQ|All Blog Posts
Phantom CodePhantom Code
Phantom Code is an undetectable desktop application to help you pass your Leetcode interviews.
All systems online

Legal

Refund PolicyTerms of ServiceCancellation PolicyPrivacy Policy

Pages

Contact SupportHelp CenterFAQBlogPricingBest AI Interview Assistants 2026FeedbackLeetcode ProblemsLoginCreate Account

Compare

Interview Coder AlternativeFinal Round AI AlternativeUltraCode AI AlternativeParakeet AI AlternativeAI Apply AlternativeCoderRank AlternativeInterviewing.io AlternativeShadeCoder Alternative

Resources

Salary GuideResume TemplatesWhat Is PhantomCodeIs PhantomCode Detectable?Use PhantomCode in HackerRankvs LeetCode PremiumIndia Pricing (INR)

Interview Types

Coding InterviewSystem Design InterviewDSA InterviewLeetCode InterviewAlgorithms InterviewData Structure InterviewSQL InterviewOnline Assessment

© 2026 Phantom Code. All rights reserved.