Clarify Requirements in LLD Interviews

Updated 2026-03-11

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.

Prerequisites: Basic understanding of software development lifecycle. Familiarity with reading problem statements and translating them into code. No specific programming language knowledge required for this topic.

After this topic: Identify ambiguous requirements in problem statements, formulate targeted clarifying questions, distinguish between core features and nice-to-haves, and establish clear success criteria before beginning implementation.

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:

  1. Prevents wasted effort: Building the wrong solution wastes precious interview time
  2. Demonstrates professionalism: Shows you think before coding, just like in real projects
  3. 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:

  1. Blocking questions: Must know to proceed (input format, core functionality)
  2. Design-impacting questions: Affect your approach (scale, performance requirements)
  3. Edge case questions: Refine your solution (null handling, boundary conditions)
  4. 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:

  1. “How many URLs do we need to support?” → Answer: 100 million
  2. “Do users need custom short URLs or just random ones?” → Answer: Random is fine for MVP
  3. “Should URLs expire?” → Answer: No expiration for now
  4. “What should happen if someone tries to shorten an invalid URL?” → Answer: Return an error
  5. “Do we need to handle the same long URL being shortened multiple times?” → Answer: Can return the same short code
  6. “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.”