How to Spend the 45 Minutes

A budget that works: 5 clarifying, 5 estimating, 10 high-level, 15 deep dive, 10 bottlenecks — and what an overrun costs.

Syntax5 clarify -> 5 estimate -> 10 high-level -> 15 deep dive -> 10 bottlenecks (45 total)

Forty-five minutes is short enough that the budget is part of the answer. A split that works: 5 minutes clarifying, 5 estimating, 10 on the high-level design, 15 on whichever deep dive the interviewer picks, 10 on bottlenecks and follow-ups.

PhaseMinutesDone when
Clarify5Scope is written where you both can see it, and agreed
Estimate5Read QPS, write QPS, storage per year
High level10One labelled path from client to datastore and back
Deep dive15The component they chose, down to keys and failure
Bottlenecks10Single points of failure named, next steps stated

Where the time actually goes

The deep dive carries the depth signal, and it is the phase that gets eaten. Ten extra minutes of requirements gathering feels productive — the questions are still sensible ones — but it is paid for out of minute 30 onwards, so the interviewer never watches you take anything to the bottom. Overrunning the high-level design does the same damage.

Say the transitions out loud

Keep a visible clock and announce each move: "that is the scope, two minutes on numbers now." It gives the interviewer an opening to redirect you early, which is almost always in your favour — they already know which deep dive they intend to ask for, and will often steer you into it if you pause.

If you are late, compress deliberately rather than continuing in order: lock the scope, keep one number, draw four boxes, and ask which part they want the remaining time spent on.

Example

Example · javascript
// The budget as data, so "where should I be right now?" has an answer.
const PHASES = [
  { name: 'clarify',     minutes: 5,  done: 'scope written down and agreed' },
  { name: 'estimate',    minutes: 5,  done: 'read QPS, write QPS, storage per year' },
  { name: 'high-level',  minutes: 10, done: 'four boxes, labelled arrows, one request walked' },
  { name: 'deep-dive',   minutes: 15, done: 'their chosen component, to keys and failure' },
  { name: 'bottlenecks', minutes: 10, done: 'SPOFs named, next steps stated' },
];

// Cumulative deadline per phase.
// Longhand: let t = 0; const schedule = []; for (const p of PHASES) { t += p.minutes;
//           schedule.push(Object.assign({}, p, { endsAtMinute: t })); }
// Shorthand below: { ...p } is object spread (ES2018), identical to Object.assign({}, p).
let elapsed = 0;
const schedule = PHASES.map((p) => ({ ...p, endsAtMinute: (elapsed += p.minutes) }));

// ?? is nullish coalescing (ES2020); Array.prototype.at is ES2022.
const phaseAt = (minute) => schedule.find((p) => minute < p.endsAtMinute) ?? schedule.at(-1);

phaseAt(12).name; // 'high-level' — if you are still clarifying here, you are behind

When to use it

  • Pacing a 45-minute loop so the deep dive is not cut short
  • Running a timed mock with a peer and scoring the pacing separately from the design
  • Recovering when requirements gathering has already taken twelve minutes
  • Deciding what to cut when the interviewer joins five minutes late

More examples

Recovering from an overrun

Handing the interviewer the choice of deep dive turns a pacing mistake into a collaboration, and they will pick the component they were going to ask about anyway. Use it the moment you notice you are more than five minutes behind.

Example · javascript
// Minute 17 and the scope is still being negotiated. Do not continue in order.
const recover = (minutesLeft) => [
  'name it: "I have spent too long on requirements, locking the scope now"',
  'keep ONE number: the read:write ratio. Drop storage and bandwidth maths',
  'draw four boxes only — client, load balancer, service, datastore — and walk one request',
  // Template literal; the longhand is '...with the remaining ' + minutesLeft + ' minutes?'
  `ask directly: "which part should I go deep on with the remaining ${minutesLeft} minutes?"`,
];

recover(28);

What each phase must leave on the board

Timing advice is hard to act on; a required artefact is not. Run this list over a photograph of your own practice whiteboard and the pacing problems show up as blank cells.

Example · javascript
// Artefacts, not activities. If a phase ends with nothing written, it did not happen.
const artefacts = {
  clarify: ['in scope', 'out of scope', 'the numbers you were given'],
  estimate: ['read QPS', 'write QPS', 'bytes per record x records per year'],
  highLevel: ['boxes', 'arrows labelled with protocol and sync/async', 'the happy path'],
  deepDive: ['the key or partition key', 'the index', 'what happens when this box dies'],
  bottlenecks: ['single points of failure', 'the first thing you would measure in production'],
};

// A quick self-check at the end of a mock round.
const missing = (board) =>
  Object.entries(artefacts).flatMap(([phase, items]) =>
    items.filter((i) => !board.includes(i)).map((i) => `${phase}: ${i}`));

Discussion

  • Be the first to comment on this lesson.