Implement Key Components in LLD Interviews
TL;DR
Implementing key components means writing working code for the critical parts of your design first, not trying to build everything at once. Focus on core functionality that demonstrates your understanding of the problem, then expand incrementally. This approach helps you manage time, catch design flaws early, and show interviewers you can prioritize effectively.
Core Concept
What Are Key Components?
Key components are the classes, methods, and relationships that form the backbone of your system. They’re the parts that:
- Directly solve the core problem
- Other components depend on
- Demonstrate your understanding of the domain
- Are most likely to reveal design flaws
In a 45-minute interview, you cannot implement everything. You must identify what matters most.
The Priority Framework
Use this hierarchy to decide what to implement:
1. Core Domain Classes — The main entities in your problem (e.g., User, Account, Vehicle). These represent the “nouns” of your system.
2. Critical Operations — The methods that perform the primary use case (e.g., transfer_money(), book_ride(), process_payment()). These are the “verbs” that make your system useful.
3. Key Relationships — How core classes interact (e.g., composition, inheritance, associations). Show one or two important relationships with actual code.
4. Essential Data Structures — Internal collections or storage mechanisms that enable core operations (e.g., a dictionary mapping user IDs to accounts).
5. Error Handling — Basic validation for the happy path. Don’t over-engineer edge cases initially.
The Implementation Strategy
Follow this sequence:
Step 1: Stub Out Core Classes — Create class definitions with key attributes and method signatures. Use comments or pass statements for method bodies initially.
Step 2: Implement One Complete Flow — Pick the most important use case and implement it end-to-end. This proves your design works.
Step 3: Add Supporting Methods — Fill in helper methods and utilities that your core flow needs.
Step 4: Expand Incrementally — If time permits, add secondary features or refine existing code.
This approach gives you a working system quickly, allowing you to demonstrate functionality and adjust based on interviewer feedback.
Visual Guide
Implementation Priority Flow
graph TD
A[Design Complete] --> B[Identify Core Classes]
B --> C[Stub Out Class Definitions]
C --> D[Choose Primary Use Case]
D --> E[Implement Core Flow End-to-End]
E --> F{Does It Work?}
F -->|No| G[Debug & Refine]
G --> E
F -->|Yes| H[Add Supporting Methods]
H --> I{Time Remaining?}
I -->|Yes| J[Expand Features]
I -->|No| K[Code Review & Explain]
J --> K
Follow this systematic approach to implement key components efficiently during interviews. Always get something working before adding complexity.
Component Priority Levels
graph LR
A[All Components] --> B[P1: Core Domain Classes]
A --> C[P2: Critical Operations]
A --> D[P3: Key Relationships]
A --> E[P4: Data Structures]
A --> F[P5: Error Handling]
A --> G[P6: Edge Cases]
A --> H[P7: Optimizations]
style B fill:#ff6b6b
style C fill:#ff8787
style D fill:#ffa07a
style E fill:#ffd93d
style F fill:#6bcf7f
style G fill:#95e1d3
style H fill:#a8dadc
Prioritize implementation from P1 (must-have) to P7 (nice-to-have). In a 45-minute interview, focus on P1-P3, touch P4-P5 if time allows.
Examples
Example 1: Parking Lot System — Identifying Key Components
Problem: Design a parking lot system that can park and remove vehicles.
Step 1: Identify Core Classes
# Core domain classes identified:
# - ParkingLot (manages the entire system)
# - ParkingSpot (represents a single spot)
# - Vehicle (represents cars, motorcycles, etc.)
# - Ticket (proof of parking)
from enum import Enum
from datetime import datetime
from typing import Optional, List
class VehicleType(Enum):
MOTORCYCLE = 1
CAR = 2
TRUCK = 3
class SpotSize(Enum):
SMALL = 1 # Motorcycles
MEDIUM = 2 # Cars
LARGE = 3 # Trucks
Step 2: Stub Out Classes with Key Methods
class Vehicle:
def __init__(self, license_plate: str, vehicle_type: VehicleType):
self.license_plate = license_plate
self.vehicle_type = vehicle_type
class ParkingSpot:
def __init__(self, spot_id: int, size: SpotSize):
self.spot_id = spot_id
self.size = size
self.vehicle: Optional[Vehicle] = None
def is_available(self) -> bool:
return self.vehicle is None
def can_fit(self, vehicle: Vehicle) -> bool:
# Map vehicle types to required spot sizes
required_size = {
VehicleType.MOTORCYCLE: SpotSize.SMALL,
VehicleType.CAR: SpotSize.MEDIUM,
VehicleType.TRUCK: SpotSize.LARGE
}
return self.size.value >= required_size[vehicle.vehicle_type].value
def park_vehicle(self, vehicle: Vehicle) -> bool:
if self.is_available() and self.can_fit(vehicle):
self.vehicle = vehicle
return True
return False
def remove_vehicle(self) -> Optional[Vehicle]:
vehicle = self.vehicle
self.vehicle = None
return vehicle
class Ticket:
def __init__(self, ticket_id: int, vehicle: Vehicle, spot: ParkingSpot):
self.ticket_id = ticket_id
self.vehicle = vehicle
self.spot = spot
self.entry_time = datetime.now()
Step 3: Implement Core Flow (Park a Vehicle)
class ParkingLot:
def __init__(self, name: str):
self.name = name
self.spots: List[ParkingSpot] = []
self.tickets: dict[int, Ticket] = {} # ticket_id -> Ticket
self.next_ticket_id = 1
def add_spot(self, spot: ParkingSpot):
"""Initialize parking lot with spots"""
self.spots.append(spot)
def park_vehicle(self, vehicle: Vehicle) -> Optional[Ticket]:
"""Core operation: Park a vehicle and return a ticket"""
# Find first available spot that fits the vehicle
for spot in self.spots:
if spot.is_available() and spot.can_fit(vehicle):
if spot.park_vehicle(vehicle):
# Create ticket
ticket = Ticket(self.next_ticket_id, vehicle, spot)
self.tickets[ticket.ticket_id] = ticket
self.next_ticket_id += 1
return ticket
return None # No available spot
def remove_vehicle(self, ticket_id: int) -> bool:
"""Core operation: Remove a vehicle using ticket"""
if ticket_id not in self.tickets:
return False
ticket = self.tickets[ticket_id]
vehicle = ticket.spot.remove_vehicle()
if vehicle:
del self.tickets[ticket_id]
return True
return False
# Expected Output:
parking_lot = ParkingLot("Downtown Lot")
# Add spots
parking_lot.add_spot(ParkingSpot(1, SpotSize.SMALL))
parking_lot.add_spot(ParkingSpot(2, SpotSize.MEDIUM))
parking_lot.add_spot(ParkingSpot(3, SpotSize.LARGE))
# Park vehicles
car = Vehicle("ABC123", VehicleType.CAR)
ticket = parking_lot.park_vehicle(car)
if ticket:
print(f"Vehicle parked. Ticket ID: {ticket.ticket_id}, Spot: {ticket.spot.spot_id}")
# Output: Vehicle parked. Ticket ID: 1, Spot: 2
# Remove vehicle
success = parking_lot.remove_vehicle(ticket.ticket_id)
print(f"Vehicle removed: {success}")
# Output: Vehicle removed: True
Try it yourself: Add a method get_available_spots_count() that returns how many spots are currently available.
Example 2: Library Management System — Incremental Implementation
Problem: Design a library system where users can borrow and return books.
Step 1: Core Classes (Stubbed)
from datetime import datetime, timedelta
from typing import Optional, List
class Book:
def __init__(self, isbn: str, title: str, author: str):
self.isbn = isbn
self.title = title
self.author = author
self.is_borrowed = False
self.borrower: Optional['User'] = None
class User:
def __init__(self, user_id: int, name: str):
self.user_id = user_id
self.name = name
self.borrowed_books: List[Book] = []
self.max_books = 3 # Business rule
class Library:
def __init__(self):
self.books: dict[str, Book] = {} # isbn -> Book
self.users: dict[int, User] = {} # user_id -> User
Step 2: Implement Primary Use Case (Borrow Book)
class Library:
def __init__(self):
self.books: dict[str, Book] = {}
self.users: dict[int, User] = {}
def add_book(self, book: Book):
self.books[book.isbn] = book
def register_user(self, user: User):
self.users[user.user_id] = user
def borrow_book(self, user_id: int, isbn: str) -> bool:
"""Core operation: User borrows a book"""
# Validation
if user_id not in self.users:
print(f"Error: User {user_id} not found")
return False
if isbn not in self.books:
print(f"Error: Book {isbn} not found")
return False
user = self.users[user_id]
book = self.books[isbn]
# Business rules
if len(user.borrowed_books) >= user.max_books:
print(f"Error: User {user.name} has reached borrowing limit")
return False
if book.is_borrowed:
print(f"Error: Book '{book.title}' is already borrowed")
return False
# Execute borrow
book.is_borrowed = True
book.borrower = user
user.borrowed_books.append(book)
return True
# Expected Output:
library = Library()
# Setup
book1 = Book("978-0134685991", "Effective Java", "Joshua Bloch")
book2 = Book("978-0132350884", "Clean Code", "Robert Martin")
library.add_book(book1)
library.add_book(book2)
user = User(1, "Alice")
library.register_user(user)
# Borrow book
success = library.borrow_book(1, "978-0134685991")
print(f"Borrow successful: {success}")
# Output: Borrow successful: True
print(f"Alice's books: {[b.title for b in user.borrowed_books]}")
# Output: Alice's books: ['Effective Java']
# Try to borrow already borrowed book
success = library.borrow_book(1, "978-0134685991")
# Output: Error: Book 'Effective Java' is already borrowed
# Output: Borrow successful: False
Step 3: Add Supporting Operation (Return Book)
class Library:
# ... previous methods ...
def return_book(self, user_id: int, isbn: str) -> bool:
"""Supporting operation: User returns a book"""
if user_id not in self.users or isbn not in self.books:
return False
user = self.users[user_id]
book = self.books[isbn]
# Check if user actually borrowed this book
if book not in user.borrowed_books:
print(f"Error: User {user.name} hasn't borrowed '{book.title}'")
return False
# Execute return
book.is_borrowed = False
book.borrower = None
user.borrowed_books.remove(book)
return True
# Expected Output:
success = library.return_book(1, "978-0134685991")
print(f"Return successful: {success}")
# Output: Return successful: True
print(f"Alice's books: {[b.title for b in user.borrowed_books]}")
# Output: Alice's books: []
Try it yourself: Add a method search_books(title_keyword: str) that returns all books whose titles contain the keyword.
Java Differences
In Java, you’d use:
ArrayList<Book>instead ofList[Book]HashMap<String, Book>instead ofdict[str, Book]- Explicit getters/setters for encapsulation
nullinstead ofNone
public class Library {
private Map<String, Book> books;
private Map<Integer, User> users;
public Library() {
this.books = new HashMap<>();
this.users = new HashMap<>();
}
public boolean borrowBook(int userId, String isbn) {
// Implementation similar to Python
}
}
C++ Differences
In C++, you’d manage:
- Memory explicitly with pointers or smart pointers (
std::shared_ptr<Book>) std::unordered_map<std::string, Book*>for dictionariesstd::vector<Book*>for lists- Destructors for cleanup
Common Mistakes
1. Trying to Implement Everything
Mistake: Starting to code every class and method from your design diagram.
Why it’s wrong: You’ll run out of time and have nothing working to show. Interviewers value a working subset over incomplete everything.
Fix: Implement one complete vertical slice (end-to-end flow) first. For a parking lot, implement park and remove completely before adding payment processing.
2. Implementing in the Wrong Order
Mistake: Starting with edge cases, optimizations, or peripheral features.
# DON'T start here:
def calculate_parking_fee_with_discounts(ticket, is_weekend, has_membership, promo_code):
# Complex pricing logic...
Why it’s wrong: You’re solving problems that don’t matter if the core doesn’t work.
Fix: Always implement the happy path first. Get park_vehicle() and remove_vehicle() working before worrying about pricing, discounts, or error messages.
3. Writing Pseudo-Code Instead of Real Code
Mistake: Using comments or pseudo-code for critical methods.
class ParkingLot:
def park_vehicle(self, vehicle):
# TODO: Find available spot
# TODO: Assign vehicle to spot
# TODO: Create ticket
pass
Why it’s wrong: The interviewer can’t verify your logic works. You’re not demonstrating coding ability.
Fix: Write actual working code for key methods. It’s okay to stub out helper methods, but core operations must be real.
def park_vehicle(self, vehicle: Vehicle) -> Optional[Ticket]:
for spot in self.spots:
if spot.is_available() and spot.can_fit(vehicle):
spot.park_vehicle(vehicle)
return Ticket(self.next_ticket_id, vehicle, spot)
return None
4. No Error Handling in Core Methods
Mistake: Assuming everything succeeds without checking.
def borrow_book(self, user_id, isbn):
user = self.users[user_id] # KeyError if user doesn't exist
book = self.books[isbn] # KeyError if book doesn't exist
user.borrowed_books.append(book)
Why it’s wrong: Your code will crash on invalid input. Shows lack of defensive programming.
Fix: Add basic validation for the happy path. Return None, False, or raise exceptions for errors.
def borrow_book(self, user_id, isbn) -> bool:
if user_id not in self.users or isbn not in self.books:
return False
# ... rest of logic
5. Ignoring the Interviewer’s Feedback
Mistake: Continuing to implement your original plan when the interviewer suggests changes.
Why it’s wrong: Interviewers test your ability to adapt and collaborate. Ignoring feedback signals poor teamwork.
Fix: When an interviewer says “What if we need to support multiple parking lots?” or “How would you handle concurrency?”, acknowledge it and adjust your implementation priority. Ask: “Should I refactor now or note it for later?”
Interview Tips
1. Announce Your Implementation Plan
Before writing code, tell the interviewer: “I’m going to implement the core classes first — ParkingLot, ParkingSpot, and Vehicle. Then I’ll implement the park_vehicle() method end-to-end. After that works, I’ll add remove_vehicle(). Does that sound good?”
Why this works: Sets expectations, shows you can prioritize, and gives the interviewer a chance to redirect you if needed.
2. Write Method Signatures First
Stub out all methods with signatures and docstrings before implementing:
class Library:
def borrow_book(self, user_id: int, isbn: str) -> bool:
"""User borrows a book. Returns True if successful."""
pass
def return_book(self, user_id: int, isbn: str) -> bool:
"""User returns a book. Returns True if successful."""
pass
Then implement one at a time. This shows you’re thinking about the complete interface.
3. Test As You Go
After implementing each key method, write a quick test:
# Implement park_vehicle()
# Immediately test it:
parking_lot = ParkingLot("Test Lot")
parking_lot.add_spot(ParkingSpot(1, SpotSize.MEDIUM))
car = Vehicle("ABC123", VehicleType.CAR)
ticket = parking_lot.park_vehicle(car)
print(f"Ticket: {ticket.ticket_id if ticket else 'Failed'}")
Why this works: Catches bugs immediately. Shows you write testable code. Gives you confidence to move forward.
4. Use Time Checks
At 20 minutes: You should have core classes defined and one method fully implemented.
At 35 minutes: You should have 2-3 key methods working and be ready to discuss trade-offs.
At 40 minutes: Stop adding features. Clean up code, add comments, prepare to explain.
Pro tip: If running behind, tell the interviewer: “I’m going to focus on finishing park_vehicle() completely rather than starting remove_vehicle() partially. Is that okay?” This shows time management awareness.
5. Narrate Your Decisions
As you code, explain your choices:
- “I’m using a dictionary here for O(1) lookup by ticket ID.”
- “I’m returning
Noneto indicate failure rather than throwing an exception because…” - “I’m storing a reference to the user in the book so we can track who borrowed it.”
Why this works: Interviewers can’t read your mind. Narration shows your thought process and invites feedback.
6. Know When to Stub
It’s okay to say: “The calculate_fee() method would compute pricing based on duration, but for now I’ll stub it out and focus on the parking logic.”
def calculate_fee(self, ticket: Ticket) -> float:
"""Calculate parking fee. Stub for now."""
return 10.0 # Placeholder
This shows you understand scope management.
7. Prepare for Follow-Up Questions
After implementing core components, interviewers often ask:
- “How would you handle concurrency?” (Answer: Add locks, use thread-safe data structures)
- “How would you persist this data?” (Answer: Add a database layer, serialize to JSON)
- “How would you scale this?” (Answer: Distribute spots across servers, use message queues)
Have high-level answers ready. You don’t need to implement these, but show you’ve thought about them.
Key Takeaways
- Prioritize ruthlessly: Implement core domain classes and critical operations first. Everything else is secondary.
- One complete flow beats many incomplete pieces: Get
park_vehicle()working end-to-end before startingremove_vehicle(). - Write real code for key methods: Pseudo-code is acceptable for helpers, but core operations must be actual, working code.
- Test incrementally: After each method, write a quick test to verify it works. Catch bugs early.
- Communicate your plan: Tell the interviewer what you’re implementing and why. Adjust based on their feedback.