Skip to content
Commitwise
Menu
Coding Interview

How coding interviews work

What happens in a coding interview, what interviewers score, a six-step method from clarifying to testing, and how to recognize which pattern a problem needs, with examples in Swift, Kotlin, Java and TypeScript.

beginner

The core idea with one simple example.Step by step, with more examples and exercises.A cheat sheet: key APIs, gotchas and trade-offs.Internals, trade-offs and production pitfalls.

You know hash maps, you’ve solved a hundred problems at home, and in the interview your mind goes blank. Or you write a working solution in ten minutes and still don’t get the offer. Both happen for the same reason: a coding interview isn’t a test of whether you can produce an answer. It’s a test of how you get there, and that part can be practiced like any other skill.

New to some of these? Open what you need, or skip ahead.

Basics:What are arrays and indexes?

An array is a row of values stored one after another: [5, 8, 2, 9]. Each position has an index, starting at 0: here 5 is at index 0 and 9 at index 3. The last index is always count - 1 (Swift) or size - 1 / length - 1 (Kotlin, Java, TypeScript).

Reading nums[2] is instant, whatever the array’s size. Reading an index that doesn’t exist (like nums[4] here) crashes with an “index out of range” error (in TypeScript it quietly gives undefined, a bug of its own), which is why loops usually go from 0 up to, but not including, the length.

Basics:What does O(n) mean?

Big-O describes how the amount of work grows with the size of the input n:

  • O(1): the same work whatever the size (reading array[5]).
  • O(log n): grows very slowly (about 20 steps for a million items).
  • O(n): grows in step with the input (one pass over the array).
  • O(n²): comparing every item with every other item, which gets slow fast.

You’ll see it next to every solution, for time and for extra space (memory). The Big-O tutorial explains it properly.

Basics:What are lists, sets and maps?

Collections hold many values:

  • A list (Swift Array, Kotlin List, Java List, a TypeScript array) keeps items in order, and allows duplicates: ["Ana", "Ben", "Ana"].
  • A set holds each value at most once and answers “is this in it?” very quickly.
  • A map (Swift Dictionary, Kotlin, Java and TypeScript Map) stores values under keys: "Ana" → 30, looked up by the key.

Lists use indexes starting at 0: in ["a", "b", "c"], "a" is at index 0 and "c" at index 2.

Part Not started

What happens, and what’s scored

Think of a driving test. The examiner doesn’t only check that you arrive at the destination. They watch you check the mirrors, signal, and slow down at the crossing. Arriving while running a red light fails the test; driving carefully and taking one wrong turn usually doesn’t.

A coding interview works the same way. You get a problem, usually about arrays, strings, maps or trees, and 30–45 minutes to solve it in a shared editor while talking to the interviewer. The answer matters, but so do the checks along the way: did you understand the problem, think about edge cases, choose a sensible approach, and test your code?

The format is standardized because it’s meant to be comparable across candidates: one to two problems in 45–60 minutes, a shared editor (often without a compiler), and a rubric with four or five dimensions. Senior candidates get the same kind of problems with a higher bar: faster to a working optimal solution, cleaner code, unprompted edge cases and complexity, and comfortable handling of follow-ups (“the input doesn’t fit in memory”, “now it’s a stream”).

At senior level, a correct solution delivered in silence often scores worse than a slightly slower one with clear reasoning, because the rubric can’t credit what it can’t see.

The four things on the scorecard

AreaWhat earns points
Problem solvingClarifies the problem, finds an approach, improves it, explains why it’s correct
CodingReadable, working code with good names, in the language you chose
VerificationTests with an example and edge cases, finds and fixes own bugs
CommunicationThinks aloud, explains trade-offs, uses hints well

Notice that only one of the four is “write code”. Many candidates who fail can code well; they jump in without asking questions, go silent, or say “done” without testing. Those are habits, and the method in the next part fixes them.

Practical details

  • Pick the language you’re fastest in. Interviewers rarely care which; use the one where you don’t have to think about syntax. Swift, Kotlin, Java and TypeScript are all fine.
  • You may not be able to run the code. Many interviews use a plain shared editor. That’s why testing by hand (tracing an example) is a skill you need to practice.
  • It’s fine to ask for a hint, or to say “I don’t remember the exact name of this method; I’ll write sortedDescending() and we can assume it exists”. Inventing confident-sounding wrong things is worse than asking.
  • Online assessments (a timed test on a website with hidden test cases) are different: there, passing all tests is what counts, and nobody hears your reasoning.

Calibrating to the level

SignalMidSenior
Time to first working solutionMost of the sessionLeaves time for follow-ups
Optimal approachReaches it with hintsReaches it independently, explains why it’s optimal
Edge casesHandles the ones promptedLists them during clarification
Follow-upsAnswers oneDiscusses trade-offs across several (memory, streaming, concurrency)
CodeWorksIdiomatic, readable, no leftover debugging

Senior loops often weight coding rounds less than design rounds, but a weak coding round still sinks a senior packet. “I’m senior, I don’t grind LeetCode” is a common way to fail an otherwise strong loop.

  • 45–60 min, 1–2 problems, often no compiler. Rubric: problem solving, coding, verification, communication.
  • Senior bar: optimal solution with time left for follow-ups, unprompted edge cases and complexity, idiomatic code, trade-off discussion under constraints (memory, streams, concurrency).
  • A silent correct answer under-scores: the rubric only credits reasoning it can see.

Check this part

  1. Which of these is scored in a coding interview?

  2. Which language should you use?

  3. What usually separates a senior coding round from a mid one?

  4. You solve it optimally in silence. What's the risk?

Usually 45–60 minutes, one or two problems in a shared editor, often without running the code. Interviewers score problem solving, code quality, testing and communication, not just a correct answer.

Part Not started

A method for every problem

  1. Clarify. Repeat the problem in your own words. Ask about the input: can it be empty? Negative numbers? Duplicates? How big? What should happen when there’s no answer?
  2. Examples. Work through one normal example by hand, and write down one or two tricky ones (empty, one element, all the same).
  3. Brute force. Say the simplest approach that works, and its complexity, even if it’s slow: “Check every pair, O(n²).”
  4. Optimize. Find what the slow version repeats, and pick a pattern that avoids it: a hash map, two pointers, sorting. Agree on the approach before coding.
  5. Code. Write it cleanly, with clear names, explaining as you go.
  6. Test. Trace your code with the small example, then the edge cases. Fix what you find. Finish with time and space complexity.

The method on a real problem

Missing Number. An array holds n different numbers from the range 0 to n, so exactly one number in that range is missing. Return it. [3, 0, 1] → 2, [0, 1] → 2, [9, 6, 4, 2, 3, 5, 7, 0, 1] → 8.

Here’s what saying it out loud sounds like:

  • Clarify: “The numbers are all different? And exactly one is missing, possibly 0 or n itself?”
  • Examples: “[0, 1] has n = 2, so the range is 0 to 2 and 2 is missing. That one’s easy to get wrong.”
  • Brute force: “For every number from 0 to n, search the array for it: n + 1 searches of n steps, O(n²).”
  • Better: “Searching is the slow part. If I put the numbers in a set first, each check is O(1): O(n) time, but O(n) extra memory.”
  • Best: “The numbers 0 to n add up to n × (n + 1) / 2. Subtract the sum of the array, and what’s left is the missing number. O(n) time and O(1) memory.”

Brute force O(n²); a hash set gets O(n) time at O(n) space; the sum formula gets O(n) time, O(1) space. Mention the overflow risk of the sum for large n on 32-bit integers, and the XOR variant that avoids it (XOR every index and value; pairs cancel, the missing number remains). Picking between the set and the formula out loud, with their trade-offs, is exactly the reasoning the rubric is looking for.

func missingNumber(_ nums: [Int]) -> Int {
    let n = nums.count
    return n * (n + 1) / 2 - nums.reduce(0, +)
}
fun missingNumber(nums: IntArray): Int {
    val n = nums.size
    return n * (n + 1) / 2 - nums.sum()
}
int missingNumber(int[] nums) {
    int n = nums.length;
    int sum = 0;
    for (int x : nums) sum += x;
    return n * (n + 1) / 2 - sum;
}
function missingNumber(nums: number[]): number {
  const n = nums.length;
  return (n * (n + 1)) / 2 - nums.reduce((a, b) => a + b, 0);
}

Test: [0, 1]: n = 2, expected 3, actual 1, answer 2. [0]: n = 1, expected 1, actual 0, answer 1. [1]: expected 1, actual 1, answer 0. Complexity: one pass, O(n) time, O(1) extra space.

Testing by hand is a skill of its own: walk through the code line by line with a tiny input, and write the variables down as they change. It catches most bugs before the interviewer does.

Practice: tracing code

Tracing means running the code in your head (or on paper) with a small input, keeping a small table of the variables:

Predict the output

What does mystery([-2, 3, -1, 2, -5]) return?

func mystery(_ nums: [Int]) -> Int {
    var best = nums[0]
    var current = 0
    for n in nums {
        current = max(n, current + n)
        best = max(best, current)
    }
    return best
}
fun mystery(nums: IntArray): Int {
    var best = nums[0]
    var current = 0
    for (n in nums) {
        current = maxOf(n, current + n)
        best = maxOf(best, current)
    }
    return best
}
int mystery(int[] nums) {
    int best = nums[0];
    int current = 0;
    for (int n : nums) {
        current = Math.max(n, current + n);
        best = Math.max(best, current);
    }
    return best;
}
function mystery(nums: number[]): number {
  let best = nums[0];
  let current = 0;
  for (const n of nums) {
    current = Math.max(n, current + n);
    best = Math.max(best, current);
  }
  return best;
}

Practice: finding a bug by testing

This function checks whether an array is sorted. It looks right. Test it with the smallest interesting input, [1, 2], line by line:

Spot the bugIs it sorted?

Tap the line with the bug.

func isSorted(_ nums: [Int]) -> Bool {
    for i in 0..<nums.count {
        if nums[i] > nums[i + 1] { return false }
    }
    return true
}
fun isSorted(nums: IntArray): Boolean {
    for (i in 0 until nums.size) {
        if (nums[i] > nums[i + 1]) return false
    }
    return true
}
boolean isSorted(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] > nums[i + 1]) return false;
    }
    return true;
}
function isSorted(nums: number[]): boolean {
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] > nums[i + 1]) return false;
  }
  return true;
}

Narrating at senior speed

The six steps compress, they don’t disappear. A senior run of Missing Number takes about a minute before coding: “Distinct values in 0…n, one missing, could be 0 or n. Brute force is n²; a set is O(n) time and space; the sum formula is O(n) time, O(1) space, but n(n+1)/2 can overflow a 32-bit int above n ≈ 46,000, so I’ll use XOR, which can’t overflow.” Then code, trace one example, state edge cases you handled, and pre-empt a follow-up.

What still costs seniors points: skipping the brute force entirely (the interviewer can’t tell whether you know why the optimal one is better), testing only the happy path, and leaving complexity for the interviewer to ask about.

  • Clarify (constraints, empty and degenerate input, duplicates, return value when there’s no answer) → examples → brute force with complexity → identify the repeated work → optimize → agree → code → trace → edge cases → complexity.
  • Compress, don’t skip: one sentence of brute force shows you know why the optimal solution is better.
  • Pre-empt the obvious follow-up (stream, fixed alphabet, memory limit).
func missingNumber(_ nums: [Int]) -> Int {
    var x = nums.count
    for (i, v) in nums.enumerated() { x ^= i ^ v }   // pairs cancel, the missing value remains
    return x
}
fun missingNumber(nums: IntArray): Int {
    var x = nums.size
    for ((i, v) in nums.withIndex()) x = x xor i xor v   // pairs cancel, the missing value remains
    return x
}
int missingNumber(int[] nums) {
    int x = nums.length;
    for (int i = 0; i < nums.length; i++) x ^= i ^ nums[i];   // pairs cancel, the missing value remains
    return x;
}
function missingNumber(nums: number[]): number {
  let x = nums.length;
  nums.forEach((v, i) => { x ^= i ^ v; });   // pairs cancel, the missing value remains
  return x;
}

Check this part

  1. What should you do before writing any code?

  2. Why say the brute-force solution out loud?

  3. How do you test code when you can't run it?

  4. You know the optimal solution immediately. Should you still mention a brute force?

  5. When should complexity be stated?

Six steps for every problem: clarify, work through examples, state a brute force, optimize, code, then test by tracing and edge cases and give the complexity. Talk through each step.

Part Not started

Recognizing the pattern

Learning to cook isn’t memorizing a thousand recipes. It’s learning a few techniques (frying, baking, boiling) and recognizing which one a dish needs. Coding problems are the same: there are thousands of them, but most are built on about fifteen patterns, and the statement usually contains a clue.

Pattern recognition is the part that compounds: once you map a problem to its family, the optimal solution and its standard pitfalls follow. Interviewers then probe whether you can adapt the pattern, not just recall it, which is why understanding the invariant behind each one matters more than the number of problems solved.

Signals in the problem statement

If the problem says…TryLesson
“seen before?”, count, pairs in unsorted dataHash map or setHash maps
sorted array, pair or triple with a sumTwo pointersTwo pointers
longest / shortest contiguous subarray or substringSliding windowSliding window
brackets, “next greater element”, undoStackStacks and queues
sorted data, “find the position”, “minimum value such that…”Binary searchBinary search
tree, levels, pathsDFS / BFSTrees
connections, dependencies, shortest pathGraph traversalGraphs
“k largest”, “k closest”, merge k sortedHeapHeaps
all combinations, permutations, subsetsBacktrackingBacktracking
number of ways, minimum cost, overlapping choicesDynamic programmingDynamic programming
meetings, ranges, overlapsSort by startIntervals
the best local choice is always safeGreedyGreedy algorithms

The input size is a hint too

Problems usually state limits like 1 ≤ n ≤ 10⁵. A typical judge runs about 10⁸ simple steps per second, so the limit tells you how fast your solution must be:

n up toTarget complexityOften means
~10O(n!)Try all permutations
~20O(2ⁿ)Try all subsets: backtracking
~500O(n³)Triple loop, some DP
~5,000O(n²)Nested loops, 2-D DP
~10⁶O(n log n) or O(n)Sorting, hash map, two pointers, window
largerO(log n) or O(1)Binary search, math

Practice: the whole method on one problem

Exerciseeasy

Best Time to Buy and Sell Stock

prices[i] is a stock’s price on day i. Choose one day to buy and a later day to sell. Return the maximum profit, or 0 if no profit is possible.

[7, 1, 5, 3, 6, 4]  → 5   (buy at 1, sell at 6)
[7, 6, 4, 3, 1]     → 0   (prices only fall)

Go through all six steps before you look at the solution: clarify, examples, brute force, optimize, code, test.

›

Brute force: try every pair of buy day i and sell day j > i. That’s O(n²). With n up to 10⁵, that’s about 5 × 10⁹ steps: too slow.

›

When you’re looking at day j as a selling day, the best buying day is simply the cheapest day before it. Can you keep track of that cheapest price as you walk through the array once?

SolutionTry it yourself firstTime O(n)Space O(1)

Walk through the prices once, remembering the lowest price so far. On each day, the best profit from selling today is today’s price minus that lowest price.

func maxProfit(_ prices: [Int]) -> Int {
    var minPrice = Int.max
    var best = 0
    for price in prices {
        minPrice = min(minPrice, price)
        best = max(best, price - minPrice)
    }
    return best
}
fun maxProfit(prices: IntArray): Int {
    var minPrice = Int.MAX_VALUE
    var best = 0
    for (price in prices) {
        minPrice = minOf(minPrice, price)
        best = maxOf(best, price - minPrice)
    }
    return best
}
int maxProfit(int[] prices) {
    int minPrice = Integer.MAX_VALUE;
    int best = 0;
    for (int price : prices) {
        minPrice = Math.min(minPrice, price);
        best = Math.max(best, price - minPrice);
    }
    return best;
}
function maxProfit(prices: number[]): number {
  let minPrice = Infinity;
  let best = 0;
  for (const price of prices) {
    minPrice = Math.min(minPrice, price);
    best = Math.max(best, price - minPrice);
  }
  return best;
}

Test with [7, 1, 5, 3, 6, 4]: minPrice goes 7, 1, 1, 1, 1, 1; best goes 0, 0, 4, 4, 5, 5. Falling prices: every price - minPrice is 0, so the answer is 0. Empty array: the loop doesn’t run, 0. Because minPrice is updated before computing the profit, selling and buying on the same day gives 0, never a negative number.

How to practice

  • Learn patterns, not problems. Solve three to five problems per pattern, then mix them, so you practice recognizing which pattern applies.
  • Time yourself (about 25 minutes per medium problem) and talk out loud, even alone.
  • Write code without running it first, then trace it, then run it. That’s the interview skill.
  • Re-solve problems a week later without looking. If you can’t, you memorized the answer instead of the idea.

Interview puzzles

Predict the output

n is up to 10⁵. Your nested-loop solution does about n²/2 simple steps, and the judge runs about 10⁸ steps per second. Roughly how long does the worst case take?

Every language has a trap that shows up in interview code. Here’s the same function in each language, each with its own classic mistake:

Spot the bugCount common elements

Tap the line with the bug.

func commonCount(_ a: [Int], _ b: [Int]) -> Int {
    var a = a, b = b
    a.sorted(); b.sorted()
    var i = 0, j = 0, count = 0
    while i < a.count && j < b.count {
        if a[i] == b[j] { count += 1; i += 1; j += 1 }
        else if a[i] < b[j] { i += 1 } else { j += 1 }
    }
    return count
}
fun commonCount(a: MutableList<Int>, b: MutableList<Int>): Int {
    a.sorted(); b.sorted()
    var i = 0; var j = 0; var count = 0
    while (i < a.size && j < b.size) {
        if (a[i] == b[j]) { count++; i++; j++ }
        else if (a[i] < b[j]) i++ else j++
    }
    return count
}
int commonCount(List<Integer> a, List<Integer> b) {
    Collections.sort(a); Collections.sort(b);
    int i = 0, j = 0, count = 0;
    while (i < a.size() && j < b.size()) {
        if (a.get(i) == b.get(j)) { count++; i++; j++; }
        else if (a.get(i) < b.get(j)) i++; else j++;
    }
    return count;
}
function commonCount(a: number[], b: number[]): number {
  a.sort(); b.sort();
  let i = 0, j = 0, count = 0;
  while (i < a.length && j < b.length) {
    if (a[i] === b[j]) { count++; i++; j++; }
    else if (a[i] < b[j]) i++; else j++;
  }
  return count;
}

Exercisemedium

The follow-up round

You solved Two Sum with a hash map in O(n). The interviewer asks three follow-ups. Answer each in two or three sentences, with the complexity:

  1. “What if the array is already sorted, and memory is very limited?”
  2. “What if the numbers arrive as a stream, and after each one you must say whether any pair so far sums to the target?”
  3. “What if the array has ten billion numbers and doesn’t fit in memory?”
›

Each follow-up removes an assumption your solution relied on: free memory, having all data up front, fitting on one machine. Name the assumption, then the change.

SolutionTry it yourself first
  1. Sorted, little memory: two pointers from both ends, O(n) time, O(1) extra space. The hash map’s O(n) memory was the cost of not having order; sorted input gives the order for free.
  2. Stream: keep a hash set of numbers seen so far; for each new x, check whether target - x is in the set, then add x. O(1) per element on average, O(n) memory over time. If memory must stay bounded, ask whether only a recent window matters (then evict old values, as in a sliding window).
  3. Doesn’t fit in memory: partition the data by value range (or by hash of the value) into files that each fit in memory, so that x and target - x land in predictable partitions, then run the in-memory solution per partition pair. Alternatively, external sort and two pointers over the sorted files. It’s O(n log n) I/O-bound work; mention that this is a MapReduce-style job.

What’s being scored is the reasoning: noticing which assumption changed and adjusting the data structure, not a memorized answer per variant.

  • Map statement signals to patterns (unsorted lookup → hash map; sorted pairs → two pointers; contiguous → window; all combinations → backtracking; overlapping subproblems → DP; ranges → sort by start; “minimum such that” → binary search on the answer).
  • Constraints → budget at ~10⁸ ops/s: n ≤ 20 → 2ⁿ, n ≤ 5,000 → n², n ≤ 10⁶ → n log n. Do the arithmetic aloud.
  • Language traps: Swift/Kotlin sorted() returns a copy; Java Integer == compares identity outside -128…127; TypeScript sort() is lexicographic without a comparator.
  • Follow-ups remove an assumption (memory, all data up front, one machine): name it, then change the structure.

Check this part

  1. The problem asks for the longest contiguous substring without repeating characters. Which pattern?

  2. n can be up to 100,000. Is an O(n²) solution likely fast enough?

  3. Why might var a = a; a.sorted() leave a unsorted?Why might a.sorted() leave a MutableList a unsorted?Why can a.get(i) == b.get(j) on List<Integer> be false for equal values?Why does [300, 10, 9].sort() give [10, 300, 9]?

  4. The interviewer says the input is now a stream. What changes first?

Most problems are variations of about fifteen patterns. Signals in the statement (sorted input, contiguous range, all combinations, k largest) and the input size point to the pattern and the complexity you need.

Key takeaways

  • A coding interview scores problem solving, code, testing and communication, not only the final answer.
  • Use the same six steps every time: clarify, examples, brute force, optimize, code, test.
  • Test by tracing a small example and edge cases (empty, one element, the last index).
  • Look for signals in the statement and the input size to pick a pattern.
  • The bar for seniors is an optimal solution with time to spare, unprompted edge cases and complexity, and fluent follow-ups.
  • Compress the steps, don’t skip them; a one-line brute force justifies the optimal one.
  • Budget complexity from the constraints out loud, and know your language’s traps.
  • Follow-ups remove an assumption: name it, then adapt the data structure.

Finished this page?Nice work, this page is complete!

Mark it as done to track your progress. It's saved in this browser, no account needed.

Its cards are now in your Bites review ⚡
Esc

Type to search the whole site.