top of page
Search

How to CONDUCT a Coding Interview

  • Writer: Rahul Sharma
    Rahul Sharma
  • May 4
  • 9 min read

A field guide for engineering leaders — how to pick the right question, read the signals that actually matter, and run a 45-minute session that tells you something real about your candidate.


I've been conducting technical interviews for most of my career — across cloud infrastructure, AI platform, and distributed systems roles. Over two decades of building engineering teams, I've interviewed hundreds of candidates and made my share of mistakes on both sides of the table.

There's an enormous amount written about how to pass a coding interview. Almost nothing is written about how to run one well. That gap matters — because a poorly run interview doesn't just waste the candidate's time. It costs your company a hire, damages your employer brand, and tells you nothing useful about the person sitting across from you.

This is a practical guide for engineering leaders and senior engineers who conduct coding interviews. I'll walk through every phase of a 45-minute session using a real question I use with my own candidates — the API Abuse Detector — as a worked example throughout.


The goal of a coding interview is not to see if someone can write perfect code under pressure. It's to have a high-quality technical conversation that reveals how someone thinks. The code is a prop. The thinking is the signal.


Why Coding Interviews Are Hard to Run Well

Most interviewers walk in underprepared. They've been handed a question by someone else, they've read the expected answer once, and they're winging the evaluation criteria. This leads to a consistent set of failures:

  • Rewarding speed over thinking. Fast coders who skip clarification aren't better engineers — they're just more comfortable with interview theater.

  • Penalizing nervousness. Experienced engineers perform worse under artificial time pressure than junior ones who interview more frequently. Nerves are not signal.

  • Looking for the "right" answer. There are almost always multiple valid approaches. An interviewer hunting for one specific solution misses the candidate who found a better one.

  • Not knowing when to hint. Letting a candidate spiral for 20 minutes on the wrong path serves no one. A good interviewer guides without giving away the answer.

Running a coding interview well is a skill that takes deliberate practice — just like any other engineering skill. Here's how I approach it.


How to Pick the Right Question

A good coding interview question has five properties. Most LeetCode-style questions have none of them.



The question I use in my own interviews is the API Abuse Detector. It's grounded in real rate-limiting infrastructure I've built, has deliberate ambiguity, supports multiple solution paths, and scales from junior to principal by adjusting the depth of optimization and tradeoff discussion I pursue.


The Example Question Used Throughout This Guide

You're running a public API platform. Every request is logged: Timestamp, UserId, EndpointId. You have two log files — one from Day 1, one from Day 2. Write a function that returns "abusive users" — users who (a) made requests on both days, AND (b) called at least two unique endpoints overall.


Structuring the 45 Minutes

Most interviewers treat the 45 minutes as one undifferentiated block of "write code." The best interviewers I know break it into deliberate phases, each with a specific purpose and clear signals they're listening for.

Phase 1

Clarification

0 – 8 min

Phase 2

Naive solution

8 – 18 min

Phase 3

Optimize

18 – 32 min

Phase 4

Edge cases

32 – 40 min

Phase 5

Discussion

40 – 45 min

Each phase has different signals you're listening for. I'll walk through each one now.



Phase 1 — Listen for Clarifying Questions (0–8 min)

Deliver the problem statement, then stop talking. What the candidate does in the next 60 seconds is one of the most revealing moments in the entire interview.


What you want to see: The candidate pauses, reads it back, and starts asking questions before touching a keyboard. This mirrors what a great engineer does when handed a ticket with incomplete requirements — they push back before they build.


For the API Abuse Detector, here are the clarifying questions I'm hoping to hear. I mentally score each one:


Clarifying question

Why it matters

Signal

Two unique endpoints — per day, or overall?

Changes the entire algorithm. "Overall" is much harder and far more interesting.

Strong signal

What about duplicate requests to the same endpoint?

Determines whether to use a List or Set for storage. Missed → wrong data structure choice.

Strong signal

Does the data fit in memory?

Unlocks the in-memory HashMap approach. Without asking, candidates can't justify their architecture.

Good signal

What takes priority — speed, memory, or maintainability?

Shows awareness that multiple valid solutions exist with different tradeoffs.

Good signal

No questions — jumps straight into coding

Red flag regardless of level. In a real system, this engineer ships the wrong thing confidently.

Concern


Your job as the interviewer in this phase: Don't answer questions that the candidate should have the instinct to ask themselves. If they ask "should I use Python?", that's a meta-question — answer it. If they ask "is the data sorted?", ask them back: "What would it mean for your approach if it were?" A great question back is often more revealing than a direct answer.


Common interviewer mistake: Giving too much away in the problem statement. If you volunteer that "both files fit in memory and you should use a HashMap," you've removed two of the most important signals the question was designed to surface.


Phase 2 — Let Them Write the Naive Solution (8–18 mins)


Most candidates will reach for a nested loop first. Let them. Don't interrupt. Don't hint. The naive solution is not the problem — failing to recognize it as naive is.


Here's what the naive O(n²) solution typically looks like:

def find_abusive_users_naive(day1_logs, day2_logs):

"""

Each log entry: (timestamp, user_id, endpoint_id)

Naive approach — nested loops, O(n²) time complexity

"""

abusive_users = []


for _, uid1, ep1 in day1_logs:

for _, uid2, ep2 in day2_logs: # ← O(n) inner loop

if uid1 == uid2:

all_endpoints = set()

for _, u, ep in day1_logs: # ← another O(n) scan

if u == uid1:

all_endpoints.add(ep)

for _, u, ep in day2_logs: # ← yet another O(n) scan

if u == uid2:

all_endpoints.add(ep)

if len(all_endpoints) >= 2:

if uid1 not in abusive_users:

abusive_users.append(uid1)

return abusive_users # Time: O(n²) — Space: O(1)

Once they finish, wait. Don't ask "is this optimal?" — that's a leading question that tells them the answer. Instead ask: "Walk me through the complexity of this solution."


What you're listening for is the self-correction — the candidate saying unprompted: "…actually the time complexity here is O(n²) and I should do better." That moment of self-awareness, without a hint, is one of the clearest signals in the entire interview. It shows algorithmic instinct and professional standards.


If they don't catch it: prompt with "What's the time complexity?" If they still don't recognize the problem after that — note it. That's a meaningful signal about how they approach performance in their day-to-day work.



Phase 3 — Watch How They Optimize (18–32 min)


This is the most diagnostic phase. The path a candidate takes from O(n²) to a better solution tells you more about their engineering depth than the solution itself.



The optimal solution uses a Dict[UserId, Set[EndpointId]]. The key insight is that the Set handles deduplication automatically, and you only need to load one file into memory — Day 2 can be streamed:

from collections import defaultdict

from typing import List, Tuple


def find_abusive_users( day1_logs: List[Tuple[str, str, str]], day2_logs: List[Tuple[str, str, str]]) -> List[str]:

"""

Time: O(n) — single pass over each file

Space: O(n) — HashMap stores Day 1 entries only

"""


# Build HashMap from Day 1: UserId → Set of unique EndpointIds

day1_map = defaultdict(set)

for _, user_id, endpoint_id in day1_logs:

day1_map[user_id].add(endpoint_id) # Set deduplicates automatically


# Stream Day 2 — no need to store it in memory

abusive_users = set()


for _, user_id, endpoint_id in day2_logs:

if user_id not in day1_map: # Condition (a): active on both days

continue


day1_eps = day1_map[user_id]


if len(day1_eps) >= 2: # Already 2+ unique endpoints → abusive

abusive_users.add(user_id)

elif endpoint_id not in day1_eps: # New endpoint from Day 2 → now 2 unique

abusive_users.add(user_id)

# else: Day 2 endpoint duplicates Day 1 → still only 1 unique, keep going


return list(abusive_users)


As the interviewer, what are you watching for? Not just whether they reach this solution — but how they get there. Did they:


  • Recognize that a Set is better than a List for the value?

  • Realize they don't need to load both files into memory?

  • Articulate that O(n) time comes at the cost of O(n) space — and that this is a deliberate tradeoff?

The candidate who reaches the right solution but can't explain a single decision they made is less hireable than the one who reaches a slightly suboptimal solution but reasons about every step out loud.



Phase 4 — Probe for Edge Cases and Correctness (32–40 min)


Once the main solution is written, I shift to probing the logic. The core of this question has a subtle three-branch decision that many candidates get partially wrong:



Two bugs I see consistently — both of which I probe for explicitly:


  • Off-by-one in the length check: Using > instead of >= means a user with exactly 2 endpoints gets incorrectly skipped. I ask: "What happens if a user hit exactly 2 unique endpoints in Day 1?"

  • Looping through the Set: Using a for loop to check membership reintroduces O(n²). The correct check is endpoint_id not in day1_eps — O(1) on a hash set.

When I spot these, I don't point them out directly. Instead I walk the candidate through a concrete test case — something like "what happens with this input?" — and let them trace through their own code. How quickly they find the bug, and whether they needed the hint, is the signal.



What Signals to Actually Score


After the interview, I don't score on "did they get the right answer." I score on six dimensions:


Dimension

What I look for

Level Bar

Ambiguity handling

Asked clarifying questions before coding. Didn't assume.

All levels

Data structure instinct

Reached for Set vs List naturally. Understood why.

All levels

Complexity awareness

Identified O(n²) without prompting and felt compelled to improve it.

All levels

Edge case thinking

Raised duplicates, off-by-one, empty inputs unprompted.

Mid+ levels

Tradeoff articulation

Explained why HashMap over sort, and named the memory cost explicitly.

Mid+ levels

Scale awareness

Noted the in-memory solution doesn't scale to petabyte logs — mentioned Spark/Kafka/MapReduce.

Senior+



Calibrating for Different Levels

The same question works across levels because you tune the depth of follow-up, not the question itself:


Junior / Mid-level (IC2–IC3)

I'm satisfied if they ask at least two clarifying questions, reach the HashMap solution with one or two hints, and correctly implement the core logic. I don't expect them to volunteer the sort-based alternative or think about distributed scale.


Senior (IC4–IC5)

I expect zero hints to reach the HashMap solution. I probe the optimization — the capped Set and early exit — and expect them to articulate the maintainability tradeoff. I ask about what happens when the log files don't fit in memory.

def find_abusive_users_optimized(day1_logs, day2_logs):

# Cap the Set at 2 — we only care about "at least 2 unique"

# Saves memory when users hit hundreds of endpoints

# Trade-off: if requirement changes to "3 unique", this silently breaks

day1_map = defaultdict(set)

for _, user_id, endpoint_id in day1_logs:

if len(day1_map[user_id]) < 2:

day1_map[user_id].add(endpoint_id)


abusive_users = set()

for _, user_id, endpoint_id in day2_logs:

if user_id in abusive_users: # Early exit — already confirmed

continue

if user_id not in day1_map:

continue

day1_eps = day1_map[user_id]

if len(day1_eps) >= 2 or endpoint_id not in day1_eps:

abusive_users.add(user_id)

return list(abusive_users)


Staff / Principal (IC6+)

I expect the full discussion: in-memory solution, its limitations at scale, and an unprompted sketch of how you'd redesign this as a distributed pipeline (Kafka topics, Flink aggregation windows, Redis for cross-day state). The coding is almost a warmup at this level — the system design conversation is where I learn the most.



Common Interviewer Mistakes

After running hundreds of interviews, these are the mistakes I see most often from the interviewer side — and what to do instead:


Mistake

What to do instead

Reading the expected answer off a sheet. You signal that you don't understand the problem yourself.

Solve the problem yourself from scratch before conducting the interview. Know every approach cold.

Giving too many hints too early. You're measuring your own helpfulness, not the candidate's ability.

Wait at least 3–4 minutes of silence before hinting. Silence is not a problem to solve.

Letting the candidate spiral for 20 minutes in the wrong direction.

If they're heading somewhere that won't yield signal, redirect with a question — not an answer.

Penalizing syntax errors in a language they don't use daily.

Tell candidates at the start: "I care about logic and reasoning, not perfect syntax."

Not adjusting expectations for interview nerves.

Engineers who interview rarely perform worse. Calibrate for that. Your best hire might be the most nervous person in the room.

Making the hire decision based on code alone.

Weight the quality of reasoning and communication heavily. Messy code + great thinking > clean code + no thinking.


The best thing you can do as an interviewer: At the start, say: "My job is to help you have a good experience here. If you're stuck, tell me — I'll guide you. I'm more interested in how you think than whether you get the perfect solution." Then mean it.


Coding interviews are imperfect by design. You're making a consequential decision about a person's career in 45 minutes under artificial conditions. The least you can do is run the process with care — designing questions that reveal real thinking, listening for the right signals, and giving candidates a fair shot to show you who they actually are.


The candidates who perform best in great interviews are the ones who would perform best on your team. That alignment is the whole point.


 
 
 

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating

© 2035 by Robert Caro. Powered and secured by Wix

bottom of page