Loading learning content...
Here's advice that feels wrong but is critically important: spend at least 10-15 minutes of a 45-minute interview NOT coding. In a timed assessment where every second counts, deliberately postponing implementation seems irrational. Yet this counterintuitive approach is exactly what distinguishes successful candidates from those who produce frantic, buggy solutions.
The instinct to start coding immediately is powerful. You feel productive when your fingers are moving. The blank editor feels like wasted time. But consider the mathematics: if you spend 10 minutes coding the wrong approach, then realize your error, you've not only lost those 10 minutes—you've lost the cognitive momentum, introduced confusion about partial code, and now must restart under greater time pressure.
Contrast this with spending those 10 minutes designing correctly. Every minute of coding thereafter moves you toward a working solution. No backtracking. No confusion. No panic.
This page teaches the design phase—the structured process of developing your solution approach before writing any code. You'll learn to identify algorithmic strategies, architect your solution, communicate your plan effectively, and build confidence before implementation begins.
The design-first approach isn't just interview strategy—it reflects how expert engineers actually work. Understanding why this phase matters helps you commit to it even when pressure mounts.
The Cost of Improvised Coding:
When you start coding without a clear plan, you're making decisions about data structures, algorithms, and edge case handling on the fly. Each decision is made under time pressure with incomplete information about how it affects later decisions. The result is code that:
'Measure twice, cut once.' In coding interviews, this translates to: design thoroughly, code once. The time spent planning isn't delay—it's prevention of much larger delays during debugging.
The design phase isn't unstructured 'thinking time.' It follows a systematic progression that ensures you consider all necessary elements before writing code.
The Design Phase Framework:
| Step | Duration | Activity | Deliverable |
|---|---|---|---|
| 1-2 min | Identify problem category and potential approaches | List of 1-3 candidate approaches |
| 2-3 min | Evaluate candidates, select best approach | Chosen algorithm with justification |
| 1-2 min | Choose structures that support the algorithm | Concrete data structure decisions |
| 3-5 min | Sketch the solution logic in plain language/pseudocode | Step-by-step solution outline |
| 1-2 min | Verify time and space complexity | Complexity confirmation |
| 1-2 min | Identify how edge cases will be handled | Edge case handling strategy |
Total Duration: 10-15 minutes
This allocation may seem long, but remember: you're spending this time whether you plan or not. The question is whether you spend it productively (planning) or chaotically (debugging).
When to Compress the Design Phase:
Some problems genuinely don't need 10+ minutes of design:
Even for familiar problems, never skip design entirely. A 2-minute verification beats discovering your memory was wrong 20 minutes into coding.
The design phase should be verbal. Think out loud. Share your reasoning with the interviewer. This has two benefits: (1) the interviewer can redirect you if you're heading wrong, and (2) you receive credit for your thinking even if time runs out before full implementation.
The first step of design is recognizing what kind of problem you're facing. Most interview problems map to a limited set of paradigms. Your mental library of patterns is your most valuable design tool.
Core Algorithmic Patterns:
| Pattern | Signal Phrases/Structures | Typical Problems |
|---|---|---|
| Two Pointers | Sorted array, pair finding, in-place modification | Two Sum (sorted), Container With Most Water |
| Sliding Window | 'Subarray', 'substring', 'window of size k' | Maximum subarray sum, minimum window substring |
| Hash Map | Frequency counting, lookup optimization, grouping | Two Sum, group anagrams, majority element |
| Binary Search | Sorted data, 'minimum/maximum that satisfies' | Search in rotated array, find peak element |
| BFS/DFS | Tree/Graph traversal, connected components | Level order traversal, number of islands |
| Dynamic Programming | 'Count ways', 'minimum/maximum', 'can we reach' | Coin change, longest subsequence, knapsack |
| Backtracking | 'All combinations', 'all permutations', 'print all' | Subsets, permutations, N-Queens |
| Greedy | Local optimal leads to global, 'earliest/latest' | Activity selection, jump game |
| Divide & Conquer | Recursive structure, merge results | Merge sort, quick sort, closest pair |
| Monotonic Stack/Queue | 'Next greater element', 'sliding window max' | Daily temperatures, sliding window maximum |
The Recognition Process:
Read the problem for pattern signals:
Consider the input structure:
Consider the output structure:
Consider constraints:
Many problems combine patterns. 'Find the longest substring with at most k distinct characters' combines sliding window (for substring) with hash map (for tracking distinct characters). Don't expect a single pattern—look for combinations.
Once you've identified candidate patterns, you must select the best approach for this specific problem. This selection process is crucial—choosing the wrong approach wastes significant time.
Selection Criteria:
The Evaluation Process:
For each candidate approach, perform a quick mental evaluation:
1234567891011121314151617181920212223242526272829
PROBLEM: Given an array and a target, find two indices summing to target. CANDIDATE 1: Brute Force (nested loops)- Complexity: O(n²) time, O(1) space- Feasibility: n ≤ 10^4 → 10^8 operations, borderline- Correctness: Definitely works- Implementation: Trivial- Edge cases: Multi-solution handling unclear- Score: Feasible but suboptimal CANDIDATE 2: Sort + Two Pointers- Complexity: O(n log n) time, O(1) or O(n) space- Feasibility: Easily passes constraints- Correctness: Works... but need original indices- Implementation: Moderate—must track original indices- Edge cases: Must handle duplicates carefully- Score: Good but annoying index tracking CANDIDATE 3: Hash Map (complement lookup)- Complexity: O(n) time, O(n) space- Feasibility: Easily passes constraints- Correctness: Works cleanly- Implementation: Straightforward- Edge cases: Handle same-element case (3+3=6)- Score: BEST CHOICE SELECTION: Hash Map approachREASONING: Optimal complexity, clean implementation, handles edge cases naturally. Worth the O(n) space trade-off.If you're uncertain which approach is best, start with brute force and optimize. A working O(n²) solution is better than an incomplete O(n) solution. You can always say 'Let me implement the straightforward approach first, then optimize if time permits.'
With your algorithm chosen, you must select the data structures that support it. The right structures make implementation natural; the wrong ones create friction.
Core Data Structure Properties:
| Need | Best Structure | Operations | Trade-offs |
|---|---|---|---|
| Fast lookup by key | Hash Map | O(1) get/set | No ordering, O(n) space |
| Ordered unique elements | TreeSet/TreeMap | O(log n) ops | More complex, ordered output |
| LIFO access | Stack | O(1) push/pop | Limited access pattern |
| FIFO access | Queue | O(1) enqueue/dequeue | Limited access pattern |
| Priority access | Heap/Priority Queue | O(log n) insert/extract | Only top element accessible |
| Fast prefix sums | Prefix Sum Array | O(1) range sum | O(n) setup, static data |
| Connected components | Union-Find | ~O(1) union/find | Specialized structure |
| Adjacency relationships | Graph (adj list/matrix) | Varies | Space vs. access trade-off |
The Selection Process:
For each major component of your algorithm, ask:
What operations do I need?
What's the dominant operation?
What information must be maintained?
What auxiliary structures support the main algorithm?
12345678910111213141516171819202122232425262728
PROBLEM: Find the longest substring with at most k distinct characters. ALGORITHM: Sliding window DATA STRUCTURE DECISIONS: 1. Window boundary tracking: - Need: Track left and right pointers - Choice: Two integer variables (left, right) 2. Character frequency in window: - Need: Count occurrences of each character in current window - Operations: Increment when adding char, decrement when removing - Choice: Hash Map (character → count) 3. Distinct character count: - Need: Know how many distinct characters in window - Options: - Track separately (increment when count goes 0→1, decrement when 1→0) - Use map.size() (if implementation removes 0-count keys) - Choice: Use map size after removing zero-count entries FINAL STRUCTURE:- int left = 0, right = 0- Map<Character, Integer> charCount- int maxLength = 0 This gives O(1) per character processed, O(n) total time.When designing out loud, explicitly state your data structure choices and why: 'I'll use a HashMap here because I need O(1) lookups when checking if the complement exists.' This demonstrates data structure knowledge and reasoning ability.
Before writing code, create a clear outline of your algorithm in plain language or pseudocode. This outline serves as a roadmap during implementation and a communication tool for the interviewer.
Why Outline Before Coding:
Outline Format:
Use numbered steps that map directly to code sections:
12345678910111213141516171819202122232425262728293031
PROBLEM: Find the Lowest Common Ancestor of two nodes in a Binary Tree APPROACH: Recursive DFS OUTLINE:1. Base case: If current node is null, return null2. Base case: If current node is p or q, return current node (We found one of our targets)3. Recursively search left subtree for p or q4. Recursively search right subtree for p or q5. Decision logic: a. If both left and right recursive calls found something: → Current node is the LCA (p and q are in different subtrees) b. If only left found something: → LCA is in left subtree, return left result c. If only right found something: → LCA is in right subtree, return right result d. If neither found anything: → p and q aren't in this subtree, return null TRACE THROUGH EXAMPLE:- Tree: 3 / \ 5 1 / \ / \ 6 2 0 8- Find LCA(5, 1): - At 3: recurse left and right - Left returns 5 (found p) - Right returns 1 (found q) - Both non-null → 3 is LCA ✓Outline Best Practices:
In most interviews, you'll outline verbally rather than writing everything down. Say something like: 'My approach will be... First, I'll... Then for each element... Finally, I'll...' Walk through your mental outline step by step while the interviewer follows along.
Before implementing, verify that your designed approach meets the problem's complexity requirements. This pre-implementation check prevents the disaster of implementing a solution only to realize it's too slow.
The Complexity Check:
1234567891011121314151617181920212223242526
PROBLEM: Find all pairs in array that sum to targetCONSTRAINT: n ≤ 10^5 APPROACH 1: Nested Loops- Outer loop: O(n)- Inner loop: O(n)- Total: O(n²) = 10^10 operations- VERDICT: Too slow for constraint ❌ APPROACH 2: Sort + Two Pointers- Sort: O(n log n)- Two pointer scan: O(n)- Total: O(n log n) = ~1.7 × 10^6 operations- VERDICT: Acceptable ✓ APPROACH 3: HashMap- Single pass: O(n)- HashMap operations: O(1) each- Total: O(n) = 10^5 operations- Space: O(n) for HashMap- VERDICT: Optimal ✓ SELECTION: HashMap approach- Time: O(n) ✓- Space: O(n) - acceptable trade-off- Meets all constraintsSpace Complexity Considerations:
Don't forget space complexity:
Communicate Your Analysis:
Before coding, state your complexity analysis to the interviewer:
'This approach will be O(n log n) time due to the sorting step, and O(n) space for the hash map. Given the constraint of n up to 10^5, this should run in well under a second. Does this complexity meet your expectations?'
This demonstrates your analytical ability and gives the interviewer an opportunity to redirect if needed.
Watch for hidden complexity: string concatenation in loops is O(n) per operation giving O(n²) total; list slicing in Python is O(k) for slice of size k; some 'O(1)' operations like Java's substring were historically O(n). Know your language's gotchas.
During design, proactively identify edge cases and decide how your algorithm will handle them. This prevents edge cases from appearing as surprises during implementation.
The Edge Case Planning Protocol:
Planning How to Handle:
For each edge case, decide in advance:
Does my algorithm naturally handle it? — If the general algorithm works for the edge case, no special code needed
Do I need a base case? — Recursive algorithms typically need explicit base cases for minimal inputs
Do I need validation? — Should I check and return early for edge cases?
What's the expected output? — Confirm correct behavior for each edge case
123456789101112131415161718192021222324252627282930313233343536
PROBLEM: Find the maximum subarray sum (Kadane's Algorithm) EDGE CASE ANALYSIS: 1. Empty array - Expected: 0 or error depending on spec - Handling: Return 0 / check at start 2. Single element - Expected: That element itself - Handling: General algorithm works ✓ 3. All negative - Expected: Largest (least negative) single element - Handling: Need to track max even when current sum resets - DANGER: Naive implementation might return 0 ❌ 4. All positive - Expected: Sum of entire array - Handling: General algorithm works ✓ 5. Mixed with zeros - Expected: Zeros might be included or excluded - Handling: General algorithm handles naturally ✓ REQUIRED MODIFICATIONS:- Initialize maxSum to first element, not to 0- This ensures all-negative case returns largest element OUTLINE UPDATE:1. If array is empty, return 0 (or per spec)2. Initialize maxSum = currentSum = nums[0]3. For each element from index 1: a. currentSum = max(nums[i], currentSum + nums[i]) b. maxSum = max(maxSum, currentSum)4. Return maxSumDemonstrate edge case awareness by voicing it: 'Before I code, let me consider edge cases. An empty array should return... A single element should... If all elements are negative, my algorithm needs to...' This shows thoroughness that interviewers value.
The design phase is not silent thinking—it's collaborative discussion. How you communicate your design significantly impacts interviewer perception.
The Communication Framework:
What to Avoid:
Sample Communication Script:
*'Alright, having understood the problem, I'm thinking about my approach. This asks for finding a pair that sums to a target in an array, which I recognize as a classic two-sum variant.
I see a couple options: brute force with nested loops at O(n²), or using a hash map where I store values I've seen and check for complements in O(n). Given that n can be up to 10^5, I'll go with the hash map approach.
My algorithm will be: iterate through the array, and for each element, check if (target - current) exists in my hash map. If yes, return those indices. If no, add the current value and its index to the map.
This gives O(n) time and O(n) space. The only edge case I need to handle is if the same element is used twice—I'll check for that by verifying indices are different.
Does this approach sound good, or would you like me to consider anything else before I start coding?'*
Always pause after presenting your approach and before coding to get interviewer buy-in. This creates a natural checkpoint where you can receive guidance if your approach is flawed. It's much better to hear 'Have you considered...' before coding than after.
Let's consolidate the key principles of effective design before coding:
Practice Framework:
To develop strong design skills:
Practice pattern recognition — For each practice problem, identify the pattern before looking at solutions. Track your accuracy.
Write outlines before coding — Even for practice, force yourself to outline in comments before implementation.
Time your design phase — Use a stopwatch to track how long you spend understanding vs. designing vs. coding. Target 30% for understanding+design.
Practice verbal design — Explain your approach out loud before coding. Record yourself and review.
Review after solving — Did your design match your implementation? Where did you deviate and why?
You now understand the critical design phase that separates elegant solutions from improvised debugging sessions. The investment in design pays dividends through cleaner implementation and fewer errors. Next, we'll explore coding with communication—how to implement your design while maintaining continuous dialogue with your interviewer.