Skip to content

Senior Engineering Interview Handbook / Chapter 34

Writing Senior-Level Interview Code

A technical-foundation chapter on senior-level coding style in interviews: naming, decomposition, helper functions, explicit assumptions, comments, minimal abstraction, and practical extensibility.

The code passes. Can anyone inspect it?

Consider a familiar prompt:

Given a list of closed intervals, merge every pair that overlaps and return the merged intervals in increasing start order.

This solution has the right algorithm:

def solve(a):
    if not a:
        return []
    a.sort()
    r = []
    s, e = a[0]
    for x in a[1:]:
        if x[0] <= e:
            e = max(e, x[1])
        else:
            r.append([s, e])
            s, e = x
    r.append([s, e])
    return r

It may pass every required test. It is still a poor artifact for a live review. The reader must discover that the input is sorted in place, remember what six short names mean, infer why touching endpoints merge, and notice that a[1:] copies part of the list. If the interviewer changes the endpoint rule, the candidate must find the policy inside the scan and explain whether anything else depends on it.

Senior-level interview code does not need to resemble a production service. It needs to make a small solution inspectable: another engineer should be able to recover the algorithm, see its ownership decisions, and locate the effect of a likely change before the clock runs out.

Begin with the decisions that enter the code

The prompt leaves two decisions worth settling before implementation. First, closed intervals that share an endpoint overlap, so [1, 3] and [3, 5] become [1, 5]. Second, the function will not change the caller’s list.

That is enough clarification. Asking about every imaginable malformed record would only delay the problem unless invalid input is in scope. A concise statement gives the reviewer the boundary:

“I’ll assume each interval is valid. I’ll treat touching endpoints as overlapping and return a new list without reordering the caller’s input.”

The code must now keep those promises. In-place sort is no longer an innocent implementation detail, and the comparison used for overlap is policy rather than punctuation.

Name state by what it means

The first improvement is not a helper or a class. It is vocabulary:

sorted_intervals = sorted(intervals, key=lambda interval: interval[0])
current_start, current_end = sorted_intervals[0]
merged = []

The names expose the state of the scan. current_start and current_end describe the interval that covers all overlapping input seen since the last output interval. merged holds intervals whose end can no longer change. sorted_intervals makes the ownership choice visible: the function has its own outer list to reorder, while the caller’s list remains in its original order.

Names need not be long. left and right are excellent names for search boundaries; row and col are clear within a grid traversal. Brevity becomes a problem when it removes the role. r tells the reader that something is stored. merged tells the reader what has already been established.

Extract the decision, not the motion

The endpoint rule deserves a name because it may change independently of the scan:

def overlaps(current_end, next_start):
    return next_start <= current_end

This helper shortens the review argument: the main loop maintains a merged range, while overlaps defines which next interval belongs to it. If the interviewer later says that touching intervals must remain separate, the policy changes from <= to < in one named place.

Not every group of lines earns extraction. Helpers named process, update, or handle_case merely force the reader to jump elsewhere. A useful helper usually names one of four things:

  • a domain rule, such as overlaps;
  • a boundary, such as in_bounds;
  • a representation, such as normalize_key;
  • an ownership event, such as copying a completed path.

The test is whether the helper makes the correctness explanation shorter. If the explanation now requires a tour through several tiny functions, leave the code inline.

Keep ownership where the reader can see it

The complete implementation is still one small function:

def merge_intervals(intervals):
    if not intervals:
        return []

    # Sort a new outer list so the caller's interval order is unchanged.
    sorted_intervals = sorted(intervals, key=lambda interval: interval[0])

    def overlaps(current_end, next_start):
        # Closed intervals that share an endpoint overlap.
        return next_start <= current_end

    interval_iter = iter(sorted_intervals)
    current_start, current_end = next(interval_iter)
    merged = []

    for next_start, next_end in interval_iter:
        if overlaps(current_end, next_start):
            current_end = max(current_end, next_end)
            continue

        merged.append([current_start, current_end])
        current_start, current_end = next_start, next_end

    merged.append([current_start, current_end])
    return merged

The iterator avoids creating the slice used in the first version. More important, each value has one visible responsibility. The active interval is mutable local state; completed intervals cross into the result as new lists. The input intervals themselves are read but never modified.

That last point is deliberately precise. sorted creates a new outer list; it does not deep-copy each interval. A shallow copy is sufficient because the function only reads the nested interval values. If the implementation later normalizes endpoints by changing those nested lists, it would violate the stated ownership promise.

The two comments remain because the syntax cannot tell the whole story. One records an ownership decision and the other records an endpoint policy. Comments such as # loop over intervals or # append the result would add no information. A decision comment should explain why the line has this shape, especially when a plausible alternative would behave differently.

Minimal abstraction leaves room to finish

It is possible to make the solution look more architectural by defining an Interval class, a comparator strategy, and a merge service. None of those objects is justified by this prompt. They introduce constructors, conversion, lifecycle, and extension points that the reviewer must understand without making the scan easier to prove.

Abstraction is useful when the problem supplies a durable boundary. A class is natural when the prompt asks for a data structure that supports repeated operations. A comparator is natural when ordering itself varies. A validation layer is natural at an untrusted input boundary. A grid traversal may benefit from a neighbors(row, col) helper because it centralizes the movement rules.

The interval problem supplies one changeable domain rule, so it gets one helper. Everything else stays close to the loop. That proportion is the design judgment.

The same restraint prevents accidental state. Suppose a one-shot prompt asks for the first non-repeating character in a string. A CharacterFrequencyService with an internal count map creates questions the prompt never posed: whether instances are reusable, when counts reset, and whether lookup is valid before ingestion. Two explicit passes inside one function are easier to understand and cannot leak counts into the next call. If the follow-up asks for a streaming interface, persistent state then becomes a requirement rather than decoration.

Design for the follow-up you can name

Extensible interview code does not predict a family of future products. It keeps the important decisions local.

Return to the interval solution. The interviewer asks for half-open intervals, where an interval ending at 3 does not overlap one starting at 3. The change is small:

def overlaps(current_end, next_start):
    # Half-open intervals that only touch do not overlap.
    return next_start < current_end

The scan, names, ownership policy, and complexity remain intact. A boundary test must change too, but there is no architecture to renegotiate. The code is adaptable because the original implementation identified a real seam, not because it installed a general strategy framework in advance.

Other prompts have different seams. Diagonal grid movement belongs in the neighbor rule. A tie-break belongs in a comparison key. Input mutation belongs at the copy boundary. Invalid-record handling belongs before the core algorithm. The useful question is not “How can I make this generic?” It is “Which rule could change without changing the algorithm?”

Read the finished artifact as a reviewer

Before testing, take one quiet pass through the code in execution order. Check that the function name states the job, names still match their roles, comments still match the decisions, and no abandoned branch or debug print survives. Then inspect the library operations: sorting costs O(n log n), the new outer list costs O(n) space beyond the output, and the scan is linear. The iterator does not create another list.

This pass is not cosmetic. A stale name can preserve an obsolete model after a mid-interview change. A stale comment can make correct code look wrong. An unmentioned copy can invalidate the space analysis. Readability is part of the evidence because it lets both engineers find those disagreements while there is still time to repair them.

You can practice this without learning a new algorithm. Take an old solution and keep its behavior and complexity fixed. Rename state by role. State its mutation policy. Extract only the helper that most shortens the correctness argument. Delete comments that narrate syntax. Then introduce one plausible follow-up and see whether it changes a named decision or tears through the whole function.

The finished code need not advertise seniority. It should simply be easy to question. Chapter 33 established why the algorithm is correct; this chapter has made that reasoning visible in the artifact. The next task is to choose tests that challenge it.