Clarify Requirements in LLD Interviews
TL;DR
Before writing any code in an interview, spend 2-3 minutes asking clarifying questions to understand constraints, edge cases, and priorities. This prevents building the wrong solution and demonstrates professional communication skills that interviewers value as much as coding ability.
Core Concept
Why Clarifying Requirements Matters
In real-world software development, requirements are rarely complete. The same is true in interviews—problem statements are intentionally vague to test your ability to identify gaps. Jumping straight to code without clarification is the #1 mistake candidates make.
Clarifying requirements serves three purposes:
- Prevents wasted effort: Building the wrong solution wastes precious interview time
- Demonstrates professionalism: Shows you think before coding, just like in real projects
- Reveals hidden complexity: Uncovers edge cases and constraints that affect your design
The Clarification Framework
Use this systematic approach to gather requirements:
1. Understand the Problem Domain
Restate the problem in your own words: “So we’re building X that does Y, correct?” This confirms basic understanding and catches misinterpretations early.
2. Identify Constraints
Ask about:
- Scale: How many users? How much data? (affects data structure choices)
- Performance: Response time requirements? (affects algorithm complexity)
- Resources: Memory limits? Can we use external libraries?
3. Clarify Input/Output
- What are valid input ranges?
- What should happen with invalid inputs?
- What format should output take?
- Are there special cases to handle?
4. Distinguish Core vs. Nice-to-Have
Explicitly ask: “What’s the minimum viable solution?” This helps you prioritize when time is limited. Interviewers appreciate candidates who can ship a working core feature over an incomplete perfect solution.
The Question Hierarchy
Not all questions are equal. Ask in this order:
- Blocking questions: Must know to proceed (input format, core functionality)
- Design-impacting questions: Affect your approach (scale, performance requirements)
- Edge case questions: Refine your solution (null handling, boundary conditions)
- Nice-to-have questions: Only if time permits (additional features, optimizations)
Visual Guide
Requirements Clarification Flow
graph TD
A[Read Problem] --> B{Understand Domain?}
B -->|No| C[Restate Problem]
C --> B
B -->|Yes| D[Identify Constraints]
D --> E[Clarify Input/Output]
E --> F[Ask About Edge Cases]
F --> G{All Critical Questions Asked?}
G -->|No| H[Prioritize Remaining Questions]
H --> G
G -->|Yes| I[Confirm Core Features]
I --> J[Begin Design]
style A fill:#e1f5ff
style J fill:#d4edda
style I fill:#fff3cd
Follow this flow to systematically gather requirements. Don’t skip to design until you’ve confirmed core features.
Core vs. Nice-to-Have Decision Matrix
graph LR
A[Feature] --> B{Required for MVP?}
B -->|Yes| C[Core Feature]
B -->|No| D{Significantly Improves UX?}
D -->|Yes| E[Nice-to-Have]
D -->|No| F[Out of Scope]
C --> G[Implement First]
E --> H[Implement if Time Permits]
F --> I[Mention but Skip]
style C fill:#d4edda
style E fill:#fff3cd
style F fill:#f8d7da
Use this mental model to prioritize features during requirements gathering.
Examples
Example 1: Design a URL Shortener
Initial Problem Statement: “Design a URL shortener service like bit.ly.”
Poor Approach (No Clarification):
import random
import string
class URLShortener:
def __init__(self):
self.url_map = {}
def shorten(self, long_url):
short_code = ''.join(random.choices(string.ascii_letters, k=6))
self.url_map[short_code] = long_url
return f"short.ly/{short_code}"
Problems with this approach:
- No collision handling (what if random code already exists?)
- No validation of input URL
- Doesn’t handle scale (in-memory dict won’t work for millions of URLs)
- Unclear what “shorten” means (custom aliases? expiration?)
Good Approach (With Clarification):
Candidate’s Questions:
- “How many URLs do we need to support?” → Answer: 100 million
- “Do users need custom short URLs or just random ones?” → Answer: Random is fine for MVP
- “Should URLs expire?” → Answer: No expiration for now
- “What should happen if someone tries to shorten an invalid URL?” → Answer: Return an error
- “Do we need to handle the same long URL being shortened multiple times?” → Answer: Can return the same short code
- “What’s the expected length of the short code?” → Answer: 6-7 characters is fine
Improved Implementation Based on Clarifications:
import hashlib
from typing import Optional
class URLShortener:
def __init__(self):
self.url_map = {} # In production: use database
self.reverse_map = {} # long_url -> short_code for deduplication
self.base_url = "short.ly/"
def _is_valid_url(self, url: str) -> bool:
"""Validate URL format (simplified)."""
return url.startswith(('http://', 'https://')) and len(url) > 10
def _generate_short_code(self, long_url: str) -> str:
"""Generate deterministic short code using hash.
Using hash ensures same long_url always gets same short_code.
Taking first 7 chars of hash gives us 62^7 = 3.5 trillion possibilities.
"""
hash_object = hashlib.md5(long_url.encode())
hash_hex = hash_object.hexdigest()
# Convert to base62 for URL-safe characters
return hash_hex[:7]
def shorten(self, long_url: str) -> Optional[str]:
"""Shorten a URL.
Returns:
Short URL string if successful, None if invalid input.
"""
# Validate input (requirement clarification #4)
if not self._is_valid_url(long_url):
return None
# Check if already shortened (requirement clarification #5)
if long_url in self.reverse_map:
return self.base_url + self.reverse_map[long_url]
# Generate short code (requirement clarification #6)
short_code = self._generate_short_code(long_url)
# Store mapping
self.url_map[short_code] = long_url
self.reverse_map[long_url] = short_code
return self.base_url + short_code
def expand(self, short_url: str) -> Optional[str]:
"""Expand a short URL back to original."""
short_code = short_url.replace(self.base_url, '')
return self.url_map.get(short_code)
# Example usage
shortener = URLShortener()
# Valid URL
result = shortener.shorten("https://www.example.com/very/long/path")
print(f"Shortened: {result}") # Output: Shortened: short.ly/a1b2c3d
# Same URL returns same short code
result2 = shortener.shorten("https://www.example.com/very/long/path")
print(f"Same URL: {result2}") # Output: Same URL: short.ly/a1b2c3d
# Invalid URL
result3 = shortener.shorten("not-a-url")
print(f"Invalid: {result3}") # Output: Invalid: None
# Expand short URL
original = shortener.expand(result)
print(f"Expanded: {original}") # Output: Expanded: https://www.example.com/very/long/path
Key Improvements from Clarification:
- Input validation based on requirement #4
- Deduplication based on requirement #5
- Deterministic hash-based generation (no collisions for same URL)
- Clear error handling
- Comments explaining design decisions
Example 2: Design a Parking Lot System
Initial Problem Statement: “Design a parking lot management system.”
Critical Clarifying Questions:
Candidate: “Let me make sure I understand the scope. First, how many parking spots are we managing?” Interviewer: “Let’s say 2000 spots across 4 floors.”
Candidate: “Are there different types of spots—compact, regular, handicapped?” Interviewer: “Yes, good question. Three types: compact, regular, and large.”
Candidate: “What vehicles do we need to support?” Interviewer: “Motorcycles, cars, and vans.”
Candidate: “What are the core operations? Just parking and leaving, or do we need payment, reservations, etc.?” Interviewer: “For now, focus on: park a vehicle, remove a vehicle, and check if spots are available. Payment is nice-to-have.”
Candidate: “Should we assign the closest available spot or let users choose?” Interviewer: “Assign automatically—closest available spot that fits the vehicle.”
Candidate: “What should happen if no suitable spot is available?” Interviewer: “Return an error indicating the lot is full for that vehicle type.”
Implementation Based on Clarifications:
from enum import Enum
from typing import Optional, List
from dataclasses import dataclass
class VehicleType(Enum):
MOTORCYCLE = 1
CAR = 2
VAN = 3
class SpotType(Enum):
COMPACT = 1 # Fits: Motorcycle
REGULAR = 2 # Fits: Motorcycle, Car
LARGE = 3 # Fits: Motorcycle, Car, Van
@dataclass
class ParkingSpot:
spot_id: str
floor: int
spot_type: SpotType
is_occupied: bool = False
vehicle_id: Optional[str] = None
@dataclass
class Vehicle:
vehicle_id: str
vehicle_type: VehicleType
class ParkingLot:
def __init__(self):
self.spots: List[ParkingSpot] = []
self.vehicle_to_spot = {} # vehicle_id -> spot_id mapping
self._initialize_spots()
def _initialize_spots(self):
"""Initialize 2000 spots across 4 floors (requirement clarification)."""
spots_per_floor = 500
for floor in range(1, 5):
# 20% compact, 60% regular, 20% large (reasonable distribution)
for i in range(100):
spot_id = f"F{floor}-C{i+1}"
self.spots.append(ParkingSpot(spot_id, floor, SpotType.COMPACT))
for i in range(300):
spot_id = f"F{floor}-R{i+1}"
self.spots.append(ParkingSpot(spot_id, floor, SpotType.REGULAR))
for i in range(100):
spot_id = f"F{floor}-L{i+1}"
self.spots.append(ParkingSpot(spot_id, floor, SpotType.LARGE))
def _can_fit(self, vehicle_type: VehicleType, spot_type: SpotType) -> bool:
"""Check if vehicle can fit in spot type."""
fit_matrix = {
VehicleType.MOTORCYCLE: [SpotType.COMPACT, SpotType.REGULAR, SpotType.LARGE],
VehicleType.CAR: [SpotType.REGULAR, SpotType.LARGE],
VehicleType.VAN: [SpotType.LARGE]
}
return spot_type in fit_matrix[vehicle_type]
def park_vehicle(self, vehicle: Vehicle) -> Optional[str]:
"""Park a vehicle in the closest available suitable spot.
Returns:
spot_id if successful, None if no spot available.
"""
# Find first available spot that fits (spots are ordered by floor)
for spot in self.spots:
if not spot.is_occupied and self._can_fit(vehicle.vehicle_type, spot.spot_type):
# Assign spot
spot.is_occupied = True
spot.vehicle_id = vehicle.vehicle_id
self.vehicle_to_spot[vehicle.vehicle_id] = spot.spot_id
return spot.spot_id
# No suitable spot found (requirement clarification)
return None
def remove_vehicle(self, vehicle_id: str) -> bool:
"""Remove a vehicle from the parking lot.
Returns:
True if vehicle was found and removed, False otherwise.
"""
if vehicle_id not in self.vehicle_to_spot:
return False
spot_id = self.vehicle_to_spot[vehicle_id]
# Find and free the spot
for spot in self.spots:
if spot.spot_id == spot_id:
spot.is_occupied = False
spot.vehicle_id = None
break
del self.vehicle_to_spot[vehicle_id]
return True
def get_available_spots(self, vehicle_type: VehicleType) -> int:
"""Check how many spots are available for a vehicle type."""
count = 0
for spot in self.spots:
if not spot.is_occupied and self._can_fit(vehicle_type, spot.spot_type):
count += 1
return count
# Example usage
lot = ParkingLot()
# Park different vehicles
motorcycle = Vehicle("M001", VehicleType.MOTORCYCLE)
car = Vehicle("C001", VehicleType.CAR)
van = Vehicle("V001", VehicleType.VAN)
spot1 = lot.park_vehicle(motorcycle)
print(f"Motorcycle parked at: {spot1}") # Output: Motorcycle parked at: F1-C1
spot2 = lot.park_vehicle(car)
print(f"Car parked at: {spot2}") # Output: Car parked at: F1-R1
spot3 = lot.park_vehicle(van)
print(f"Van parked at: {spot3}") # Output: Van parked at: F1-L1
# Check availability
available = lot.get_available_spots(VehicleType.CAR)
print(f"Available spots for cars: {available}") # Output: Available spots for cars: 1599
# Remove vehicle
removed = lot.remove_vehicle("C001")
print(f"Car removed: {removed}") # Output: Car removed: True
# Try to remove non-existent vehicle
removed = lot.remove_vehicle("C999")
print(f"Non-existent car removed: {removed}") # Output: Non-existent car removed: False
What the Clarifications Achieved:
- Clear scope: 2000 spots, 4 floors, 3 vehicle types
- Defined spot assignment logic (automatic, closest available)
- Established core features vs. nice-to-have (parking/removal vs. payment)
- Clarified error handling (return None when full)
- Understood vehicle-to-spot compatibility rules
Try it yourself: Add a method to find which floor has the most available spots for a given vehicle type. What clarifying questions would you ask about this feature?
Common Mistakes
1. Jumping Straight to Code
Mistake: Starting to write code immediately after reading the problem.
Why it’s wrong: You’ll likely build the wrong solution or miss critical constraints. Interviewers are testing your requirement-gathering skills as much as your coding.
Example: Given “design a cache,” immediately implementing an LRU cache without asking about size limits, eviction policies, or thread-safety requirements.
Fix: Always spend the first 2-3 minutes asking questions. Say: “Before I start coding, let me clarify a few things…“
2. Asking Too Many Trivial Questions
Mistake: Asking about every tiny detail, including things you can reasonably assume.
Why it’s wrong: Wastes time and shows poor judgment about what matters. Interviewers expect you to make reasonable assumptions for minor details.
Example:
- “Should variable names be camelCase or snake_case?” (doesn’t matter)
- “Should I add docstrings?” (just do it if you have time)
- “Do you prefer classes or functions?” (your choice unless specified)
Fix: Focus on questions that affect your design or algorithm choice. For minor details, state your assumption: “I’ll assume input is always valid unless you’d like me to add validation.”
3. Not Distinguishing Core from Nice-to-Have
Mistake: Treating all features equally and trying to implement everything perfectly.
Why it’s wrong: You’ll run out of time before completing a working solution. Interviewers want to see you ship a minimal working version first.
Example: Spending 20 minutes on input validation and error messages when the core algorithm isn’t implemented yet.
Fix: Explicitly ask: “What’s the MVP here?” or “If we’re short on time, which features are must-haves?” Then implement core features first, adding nice-to-haves only if time permits.
4. Not Confirming Understanding
Mistake: Asking questions but not restating your understanding to confirm.
Why it’s wrong: Miscommunication can lead to solving the wrong problem entirely. The interviewer might think you understood when you didn’t.
Example: Interviewer: “The system should handle concurrent requests.” Candidate: nods and starts coding without clarifying what “handle” means
Fix: After asking questions, restate: “Just to confirm, we need thread-safe operations with proper locking, correct?” This catches misunderstandings early.
5. Ignoring Constraints Until Too Late
Mistake: Not asking about scale, performance, or resource constraints upfront.
Why it’s wrong: These constraints fundamentally affect your choice of data structures and algorithms. Discovering them late means rewriting your solution.
Example: Implementing a solution with O(n²) complexity, then learning the input size is 1 million elements.
Fix: Always ask early:
- “What’s the expected input size?”
- “Are there performance requirements?”
- “What are the memory constraints?”
- “How many users/requests per second?”
These questions should be in your first batch of clarifications.
6. Not Writing Down Requirements
Mistake: Relying on memory for all the requirements and constraints discussed.
Why it’s wrong: You’ll forget important details mid-implementation, leading to bugs or incomplete solutions.
Example: Interviewer mentions “handle null inputs” during clarification, but you forget and don’t add null checks.
Fix: Write down key requirements on the whiteboard or in comments:
# Requirements:
# - Support 1M URLs
# - 6-7 char short codes
# - Same URL -> same short code
# - Return None for invalid URLs
# - No expiration needed
This serves as a checklist and shows organized thinking.
Interview Tips
1. Use a Clarification Template
Memorize this question sequence for any design problem:
Functional Requirements:
- “What are the core features we need to support?”
- “Are there any features that are nice-to-have but not critical?”
Scale & Performance:
- “How many [users/requests/items] do we need to handle?”
- “What are the performance requirements? (latency, throughput)”
Constraints:
- “Are there any memory or storage limitations?”
- “Can we use external libraries or should this be from scratch?”
Edge Cases:
- “How should we handle invalid inputs?”
- “What should happen when [specific edge case]?”
Having this mental checklist ensures you don’t forget critical questions under pressure.
2. Signal Your Thought Process
Interviewers can’t read your mind. Narrate your clarification process:
Good: “I’m thinking about scale first because that will determine whether I use a simple array or need a more sophisticated data structure. How many items are we expecting?”
Bad: Silently thinking, then suddenly asking “How many items?”
The first approach shows structured thinking. The second leaves the interviewer wondering if you’re just guessing.
3. Make Reasonable Assumptions When Stuck
If the interviewer says “What do you think?” or “You decide,” don’t freeze:
Template: “I’ll assume [reasonable assumption] for now, and we can adjust if needed. Does that sound reasonable?”
Example: “I’ll assume the input is always valid for now so we can focus on the core algorithm. We can add validation later if time permits. Does that work?”
This shows decisiveness while keeping the door open for correction.
4. Prioritize Questions by Impact
Not all questions are equally important. Ask high-impact questions first:
High Impact (ask first):
- Scale (affects data structure choice)
- Core functionality (defines the problem)
- Performance requirements (affects algorithm choice)
Medium Impact (ask second):
- Edge cases (affects robustness)
- Error handling (affects completeness)
Low Impact (ask last or assume):
- Code style preferences
- Minor implementation details
- Features clearly outside MVP scope
If you only have time for 3 questions, make them high-impact ones.
5. Demonstrate Domain Knowledge Through Questions
Your questions can showcase expertise:
Generic: “How should we store the data?”
Expert-level: “Given the scale, should we consider sharding the data? And do we need to optimize for read-heavy or write-heavy workloads?”
The second question shows you understand distributed systems concepts. Even if the interviewer says “let’s keep it simple,” you’ve demonstrated deeper knowledge.
For OOP design problems, ask:
- “Should this be thread-safe?” (shows concurrency awareness)
- “Do we need to support extensibility for future vehicle types?” (shows design pattern knowledge)
- “Should we favor composition or inheritance here?” (shows OOP principles understanding)
6. Confirm Before Coding
Before writing any code, do a final confirmation:
Template: “Let me summarize what I understand before I start coding:
- [Core feature 1]
- [Core feature 2]
- [Key constraint]
- [Important edge case]
Does that match your expectations?”
This 30-second investment prevents 20 minutes of coding in the wrong direction.
7. Time Management
For a 45-minute interview:
- Minutes 0-3: Clarify requirements (don’t exceed 5 minutes)
- Minutes 3-8: Design approach and discuss trade-offs
- Minutes 8-35: Implement solution
- Minutes 35-40: Test and handle edge cases
- Minutes 40-45: Discuss optimizations or answer follow-ups
If you’re past minute 5 and still asking questions, you’re spending too much time. Make assumptions and move forward.
8. Red Flags to Avoid
Don’t:
- Ask questions you can Google (“What does LRU stand for?”)
- Challenge the problem’s validity (“This wouldn’t happen in real life”)
- Ask about the company’s actual implementation (“How does Google do this?”)
- Get defensive if your assumption was wrong (“Well, you didn’t tell me that!”)
Do:
- Ask questions that show you’re thinking about real-world implications
- Accept corrections gracefully: “Good point, let me adjust my approach”
- Show enthusiasm for the problem
9. Practice Common Clarification Scenarios
Some problems have standard clarifications. Memorize these:
For “Design a Cache”:
- Eviction policy? (LRU, LFU, FIFO)
- Max size?
- Thread-safe?
- TTL (time-to-live) needed?
For “Design a Rate Limiter”:
- Per user or global?
- Time window? (per second, minute, hour)
- What happens when limit exceeded? (block, queue, return error)
- Distributed or single-machine?
For “Design a Parking Lot”:
- Vehicle types?
- Spot types?
- Assignment strategy? (closest, user choice, reserved)
- Payment needed?
Having these memorized lets you ask comprehensive questions quickly.
10. Turn Clarifications Into Design Decisions
As you ask questions, start forming your design:
Example dialogue:
You: “How many URLs do we need to support?” Interviewer: “100 million.” You: “Got it. That means an in-memory hash map won’t work—we’ll need a database. And for the short code, 6 characters in base62 gives us 56 billion possibilities, which is plenty. I’ll use a hash-based approach to ensure the same URL always gets the same short code.”
This shows you’re not just collecting information—you’re actively using it to make design decisions. That’s what separates senior engineers from junior ones.
Key Takeaways
-
Spend 2-3 minutes clarifying before coding: This prevents building the wrong solution and demonstrates professional communication skills that interviewers value highly.
-
Use the clarification framework systematically: (1) Understand the domain, (2) Identify constraints (scale, performance, resources), (3) Clarify input/output, (4) Distinguish core features from nice-to-haves.
-
Ask high-impact questions first: Prioritize questions about scale, core functionality, and performance requirements—these affect your fundamental design choices.
-
Confirm your understanding before coding: Restate the requirements in your own words to catch misunderstandings early: “Just to confirm, we need X, Y, and Z, correct?”
-
Make reasonable assumptions when needed: If the interviewer defers to you, state your assumption clearly and move forward: “I’ll assume valid input for now so we can focus on the core algorithm. We can add validation later if time permits.”