Skip to content

Senior Engineering Interview Handbook / Chapter 32

The Coding-Round Workflow

An interview-round skill chapter that teaches the operating sequence for senior coding rounds: clarify, model constraints, propose and improve a solution, state invariants, implement, test, analyze complexity, and discuss productionization.

Keep the work visible

Eighteen minutes into a coding round, a candidate can have a plausible idea and still be in trouble. The contract is half stated. Code has begun, but the interviewer does not know which assumptions it implements. No test has been reserved for the boundary that determines whether the main comparison is < or <=.

The danger is not simply that the code may be wrong. The round has become hard to steer. A useful coding workflow keeps enough of the reasoning in view that either person can correct the course before time runs out.

A coding workflow moves through clarify, model, plan, implement, test, and analyze, with test results returning to implementation.
The sequence creates visible checkpoints, but evidence from testing can send the work back to an earlier decision.

The movement is simple:

  1. Restate the problem as an implementable contract.
  2. Identify the inputs, output, and assumptions that can change the code.
  3. Use a small example to test that understanding.
  4. Read the constraints before choosing an approach.
  5. Give a correct baseline, then identify its bottleneck.
  6. Improve the plan only as far as the constraints require.
  7. State the invariant that will keep the implementation honest.
  8. Implement in coherent pieces.
  9. Test the cases most likely to break the model.
  10. Analyze the code that actually exists.
  11. Name the production concerns specific to the problem.

Those actions are ordered, but they are not a recital. A revealing example may send you back to the contract. A failing test may expose a bad representation rather than a local typo. The workflow gives those returns a known destination.

Spend the clock on uncertainty

In a 45-minute round, the opening should be brief without being hurried. A reasonable first pass spends a few minutes on the contract and examples, then a few more on constraints and a plan. That should leave most of the round for implementation, with protected time near the end for tests and analysis.

The exact split depends on where the risk is. An unfamiliar dynamic-programming state may deserve more planning. A straightforward implementation with awkward boundaries may deserve more testing. What matters is that the clock changes your behavior before it expires. If implementation is still tangled halfway through the round, stop adding code and recover the model. If testing has not begun with several minutes left, finish a coherent core and test its most dangerous transition.

Timeboxing is useful because it forces decisions. It should never force you to pretend that an unresolved assumption has been settled.

One round, from prompt to evidence

Consider this prompt:

Given a collection of meeting intervals, return the minimum number of rooms required.

The algorithm is familiar. That makes it a good test of workflow: there is nowhere for weak framing or vague communication to hide.

Clarify until the comparison operator is determined

A weak restatement is “I need to find overlapping meetings.” It discards the output and leaves “overlap” undefined.

A better opening moves the prompt toward code:

I need the maximum number of meetings active at the same time. The input is a collection of start and end times, and the output is an integer room count. May a meeting ending at 10 share a room with one starting at 10?

Suppose the interviewer says yes: intervals are half-open, [start, end), and every meeting has start < end. Then two examples establish the boundary:

  • [(0, 10), (10, 20)] needs one room.
  • [(0, 10), (9, 20)] needs two.

Ask whether the input may be reordered before choosing an in-place sort. Ask about invalid intervals only if the prompt has not already ruled them out. Questions earn their time when an answer changes the implementation; a tour of every imaginable edge case does not.

Let scale choose how much algorithm you need

One baseline is to consider each meeting’s start time and scan every interval to count how many meetings are active then. A maximum must occur when some meeting starts, so this can produce the right answer, but the repeated scans grow quadratically.

If the input may contain 100,000 meetings, repeated pairwise comparison is the bottleneck. Sorting all start times and all end times gives a chronological sweep. A start consumes a room. An end releases one. At equal timestamps, the end must be processed first because the contract permits immediate reuse.

That is the improvement in full: sorting buys access to the next event. There is no need to list every interval pattern you remember or propose a more elaborate structure that the constraints do not ask for.

Before coding, make the plan executable:

  • Copy and sort the starts and ends so the caller’s collection is unchanged.
  • Walk both arrays with separate indexes.
  • If the next start is earlier than the next end, allocate a room and update the peak.
  • Otherwise, release a room before considering that start.

The invariant is short enough to use while debugging: before each comparison, active is the number of started meetings whose ends have not yet been processed, and peak is the largest such value seen so far. Chapter 33 develops invariant reasoning in depth. Here its job is to connect the explanation to the state the code is about to mutate.

Implement the plan you announced

def minimum_rooms(intervals):
    if not intervals:
        return 0

    starts = sorted(start for start, _ in intervals)
    ends = sorted(end for _, end in intervals)

    start_index = 0
    end_index = 0
    active = 0
    peak = 0

    while start_index < len(starts):
        if starts[start_index] < ends[end_index]:
            active += 1
            peak = max(peak, active)
            start_index += 1
        else:
            active -= 1
            end_index += 1

    return peak

The useful narration is attached to the decision:

I use strict < for a start. When a start equals the next end, the else branch releases the room first, which matches the half-open interval contract.

There is little value in announcing that you are incrementing an index or writing a while loop. The interviewer can see syntax. Narrate the facts that make a branch correct, the ownership of mutable state, and any deliberate departure from the plan.

Names should carry the vocabulary of the explanation into the code. If the plan talks about starts, ends, active rooms, and a peak, names such as i, j, count, and answer make the interviewer translate unnecessarily.

Test by attacking the model

Begin with the boundary that determined the comparison. For [(0, 10), (10, 20)], the end at 10 is processed before the start at 10, so the result remains one. Changing < to <= would allocate the second room too early and expose the bug immediately.

Then choose a few cases with distinct jobs:

  • An empty collection returns zero and exercises the guard.
  • [(0, 30), (5, 10), (15, 20)] returns two and shows that rooms can be released and reused while a longer meeting remains active.
  • [(1, 8), (2, 7), (3, 6)] returns three and drives active to its peak.
  • An unsorted input returns the same answer, confirming that the chronological model does not depend on caller order.

Trace state, not just outputs. At each event, say which pointer moves and why the invariant still holds. When a test fails, first ask which assumption it contradicts. A contract error calls for clarification; an invariant error may call for a different update; only a local implementation error deserves a local patch.

Close on the code that exists

Let n be the number of meetings. Constructing and sorting the two arrays takes O(n log n) time. The sweep advances one of its indexes at each step and takes O(n) time, so total time is O(n log n). The copied arrays use O(n) auxiliary space; sort implementation details may add runtime-specific working space.

That account includes the copies the code actually made. Claiming constant space because the sweep uses four scalar variables would describe a different implementation.

A production note should be equally specific. Real scheduling data needs a defined timestamp representation and policy for invalid or zero-duration records. Recurrence, cancellation, room capabilities, and time-zone conversion may change the data model; they are not reasons to build a calendar service inside an algorithm round. Name the boundary, then stop.

Recover without losing the thread

Good rounds rarely follow the plan without correction. Senior-level control shows most clearly when something goes wrong.

If you began coding before resolving an assumption, pause explicitly:

I started implementing before fixing the endpoint rule. I want to settle that now because it determines the comparison and the boundary test.

If the implementation has become tangled, return to the last stable statement:

This representation no longer makes the invariant easy to maintain. I am going back to the two sorted event streams, even though it means replacing this partial code.

If a test fails, report the evidence before editing:

The touching-interval case overcounts, so I am processing equal start and end times in the wrong order. The fix belongs in that tie decision.

If time is short, reduce ambition without concealing it:

I will finish and test the O(n log n) solution. I can describe the remaining validation policy afterward rather than interrupt a coherent implementation.

These statements are useful because each names the current state, the violated assumption or risk, and the next action. Apology, silent thrashing, and a sudden rewrite give the interviewer none of that information.

Practice the handoffs

Full mock interviews are valuable, but the transitions deserve isolated practice. Take a solved problem and rehearse only the first three minutes: restatement, input and output, one code-affecting question, and one boundary example. For another problem, stop after the baseline and explain exactly what work the improvement removes. For a third, begin with flawed code and practice reporting a failed test before changing anything.

Occasionally run a full problem with two alarms: one for the point at which implementation should be coherent, and one for the point at which testing must begin. Review the recording by asking where the interviewer first lost access to your reasoning. The useful measure is not how many sentences you spoke. It is whether each transition left behind something inspectable: a contract, a constraint, a plan, an invariant, working code, or test evidence.

Field reference

Coding-round workflow

  • Clarify the input, output, boundary rules, ownership, and no-answer behavior that can change code.
  • Use one ordinary example and one ambiguity-revealing example.
  • Let constraints accept or reject the baseline before implementation.
  • Name the bottleneck, improvement lever, representation, and invariant.
  • Implement in coherent pieces; narrate decisions and state transitions rather than syntax.
  • Protect time for tests that attack the contract and invariant.
  • Analyze the implementation you wrote, including sorting, copying, output, recursion, and expected or amortized assumptions where relevant.
  • End with one or two production concerns that belong to this problem.

The workflow has done its job when the final answer is not a surprise. The interviewer has watched the contract become a plan, the plan become code, and the code survive evidence strong enough to support the claims made for it.