Introduction
Code Review Interviews
The code review round - what it tests (threading, concurrency, memory, runtime depth), how to approach a snippet, and the signals that separate a pass from a strong pass.
The code review round is the format where the interviewer hands you a snippet - sometimes a real bug from their codebase, sometimes a synthetic example - and asks you to find the bug, explain the root cause, and discuss the fix. It tests depth that algorithmic and coding-challenge rounds do not reach: threading, concurrency, memory model, runtime internals, and your ability to reason about a system you did not write.
A candidate who aces LeetCode can fail this round. The skills are different: you are not inventing a solution, you are diagnosing one. The interviewer wants to see how you read code, how deep you can go, and whether you can tell the difference between a real bug and a style complaint.
What they are testing
| Area | What the interviewer wants to see |
|---|---|
| Threading | Can you spot a race condition, a deadlock, a missing lock, a visibility guarantee that does not hold? |
| Concurrency | Can you reason about ordering, atomicity, and what happens when two threads interleave? |
| Memory | Can you find a leak, a stale reference, an off-by-one in a buffer, a use-after-free? |
| Runtime / VM | Can you explain what the language runtime does: GC, escape analysis, integer overflow, string interning? |
| Systems reasoning | Can you discuss the trade-off between correctness and performance, and name when you would accept a weaker guarantee? |
The round is not about finding the most bugs. It is about finding the right bug, naming its root cause precisely, and discussing the fix at increasing depth. An interviewer who keeps asking "and what else?" is testing whether you can go deeper - that is a good sign, not a sign you missed something.
How to approach a snippet
1. Read the whole thing first
Do not start naming bugs on line 2. Read the entire snippet once, silently, and form a hypothesis about what it is trying to do. Then state it: "This looks like a thread-safe counter with lazy initialization. The bug is likely in the initialization path." This frames the rest of the conversation.
2. Name the root cause, not the symptom
A symptom is "the counter is sometimes wrong." A root cause is "the
double-checked locking pattern reads instance without a memory barrier, so
thread B can see a non-null reference before the constructor has finished
writing its fields." The second answer is what passes the round.
Work from symptom to root cause explicitly:
- What goes wrong? Name the observable behavior.
- When does it happen? Name the interleaving or condition.
- Why does the language allow it? Name the memory model, the visibility guarantee, or the atomicity gap.
- What is the fix? Name the minimal change and what guarantee it restores.
3. Discuss the fix at increasing depth
After you name the root cause, the interviewer will ask how you would fix it. Give the minimal fix first - the one-line change that removes the bug. Then discuss the deeper fix: the structural change that makes the class of bug impossible, not just this instance.
For a race on a shared counter:
- Minimal fix: add a lock around the increment.
- Deeper fix: use an atomic integer, which removes the lock and the contention, and makes the atomicity guarantee visible in the type.
- Deepest fix: redesign so the counter is not shared - per-thread counters aggregated on read, which removes the contention entirely.
The interviewer is not looking for the deepest fix. They are looking for your ability to walk down the depth ladder and explain the trade-off at each step.
Where the common bugs live
Threading and concurrency
- Race condition: two threads read-modify-write a shared field without synchronization. The fix is a lock, an atomic, or removal of the shared state.
- Deadlock: two locks acquired in opposite orders in two threads. The fix is a global lock ordering or a single lock.
- Double-checked locking: the classic Java pitfall. The reference can be
non-null before the constructor finishes. The fix is
volatile(Java 5+) or an inner-class holder. - Visibility: thread A writes a flag, thread B reads it, but B does not see
the write because there is no happens-before relationship. The fix is a
memory barrier (
volatile,synchronized,Atomic*). - Wait/notify without a loop:
wait()can wake up spuriously. The fix is to always wait inside awhile (condition) wait()loop.
Memory
- Leak via a long-lived collection: a static map holds references to objects
that are never removed. The fix is a
WeakHashMapor explicit removal. - Stale reference in a callback: a listener is registered but never unregistered, keeping a large object alive. The fix is removal on teardown.
- Off-by-one in a buffer:
copy(src, dst, len)wherelenis the source length butdstis smaller. The fix is to bound on the destination. - Use-after-free / close: a file or connection is closed but a reference is still held and used. The fix is to null the reference or scope the resource with try-with-resources.
Runtime and language
- Integer overflow:
a + boverflows silently in most languages. The fix isMath.addExact(Java), checked arithmetic (Swift), or a wider type. - String identity vs equality:
==on strings compares references, not values. The fix is.equals(), or intern the strings if you control both sides. - Autoboxing:
Map<Integer, V>withintkeys can create extraIntegerobjects. The fix is a primitive-specialized map orInteger.valueOfreuse. - Escape analysis: a local object escapes into a shared field, defeating stack allocation. The fix is to not share it, or to make it explicitly immutable.
Signals to demonstrate
| Signal | What it looks like |
|---|---|
| Precision | You name the exact line, the exact interleaving, the exact guarantee that fails. |
| Depth | You can go from symptom to root cause to minimal fix to deeper fix without prompting. |
| Trade-off awareness | You can say "this fix adds contention; that fix removes the shared state but adds allocation; I would choose this one because..." |
| Restraint | You do not rename variables, reformat, or propose style changes that do not fix a bug. |
| Honesty | When you do not know, you say "I am not sure about the visibility guarantee in this language; let me reason from first principles." |
Common mistakes
- Naming style issues as bugs: "this variable name is unclear" is not a bug. The interviewer wants a defect, not a lint pass.
- Jumping to the fix before naming the root cause: the interviewer cannot evaluate your diagnosis if you skip it. State the root cause, then the fix.
- Staying at the symptom level: "it crashes" is a symptom. Keep going until you reach the language or system guarantee that fails.
- Over-fixing: rewriting the snippet is not the task. Find the bug, name the minimal fix, discuss the deeper fix. Do not redesign the class.
- Guessing the language runtime: if you do not know whether the language guarantees atomicity for a field, say so and reason about both cases. A wrong claim about the runtime is worse than an honest "I would check the spec."
How to prepare
The round rewards depth over volume. Pick a language you will interview in and learn its memory model cold: what is atomic, what is visible, what is ordered. Then practice on real bugs - open-source issue trackers are a good source, especially concurrency-related issues with minimal reproducers.
A useful drill: take a small concurrent class you wrote, remove one lock or
one volatile, and explain exactly what can break, when, and why. If you
cannot, you do not yet understand the guarantee that the keyword was
providing.
Practice table
Practice articles for the code review round are coming. For now, use the coding-challenge interviews to build the state-model intuition that makes spotting a bug faster, and the interview protocol for the in-room conversation.
- Start a coding mock - rehearse the conversation and the diagnosis loop.
- Read the interview protocol - the clarify-plan-build-verify loop adapts to a review round.
- Browse coding challenges - build the state-model intuition that makes bugs easier to spot.
Done reading? Mark it so it sticks in your dashboard.