Practice interviews

Why did a readable patch become slow?

Find an N+1 query pattern and defend a bounded repair.

The question

Patchwell is a fictional team that builds scheduling software. This is an authored practice case. A shift list now loads each assigned employee inside a loop. The API returns up to 100 shifts. Employee IDs may repeat.

Measured queries
ShiftsQueries
Small team56
Larger team100101

Authored example. Values are supplied for this case.

Implementation

Load shifts once. For each shift, await getEmployee(shift.employeeId).

Constraint

The database supports one batched employee lookup by ID. Missing employees must display “Unassigned”.

“What explains the query growth? How would you repair it and check for regressions?”

A strong answer, step by step

Open each step in turn. This is one strong answer, not the only one.

Step 1: Describe the cost

What would you do here? Open the move you’d make.

  • Blame JavaScript iteration speed

    Tempting, but it misses something. The evidence concerns database calls.

  • Count queries as 1 + n

    The strong move. It matches both observations.

See how a strong candidate says it

“The implementation makes one shift query plus one employee query per shift: n + 1. At 100 shifts that is 101 queries, even when IDs repeat.”

What a strong candidate could say

Why it works: A measured relationship is stronger than a vague claim that loops are slow.

Step 2: Batch the lookup

What would you do here? Open the move you’d make.

  • Batch unique IDs and join by key

    The strong move. Repeated IDs need not create repeated queries.

  • Use Promise.all for all 100 employee calls

    Tempting, but it misses something. Concurrency may reduce waiting but keeps the query load.

See how a strong candidate says it

“I would collect unique employee IDs, load them in one batch, build a map by ID, then map shifts in their original order. The normal path should need two queries.”

What a strong candidate could say

Why it works: It removes per-row I/O and preserves display ordering.

Step 3: Preserve missing values

What would you do here? Open the move you’d make.

  • Zip shifts with the batch result by index

    Tempting, but it misses something. Deduplication and missing rows break positional alignment.

  • Join by ID and test missing records

    The strong move. Result order is not a reliable identity.

See how a strong candidate says it

“If an employee is missing, use the required Unassigned label. I would test repeated IDs, missing employees, an empty shift list, and stable shift order. The map must be keyed by ID, not by batch-result position.”

What a strong candidate could say

Why it works: An optimization must preserve behavior.

Step 4: Set a measured bar

What would you do here? Open the move you’d make.

  • Measure queries and latency on the same data

    The strong move. Both server load and user waiting matter.

  • Report only fewer lines of code

    Tempting, but it misses something. Line count does not establish performance.

See how a strong candidate says it

“I would compare query count for 5 and 100 shifts and check response latency on the same workload. I would not promise a constant query count for unlimited input: batching limits and pagination still need a contract.”

What a strong candidate could say

Why it works: It proves the local improvement without overstating scale.

The answer, in short

Decision
Replace per-shift lookups with a keyed batch lookup.
Because
The measured query count grows by one for every shift.
What would change it
If the storage API has a strict batch limit, split the requested page into bounded batches.

Common traps

  • Treating parallel calls as fewer calls.
  • Joining by array position after deduplication.

Check your answer

  • Did you derive n + 1 from the observations?
  • Did you preserve shift ordering?
  • Did you handle repeated and missing IDs?
  • Did you measure load as well as latency?

Last reviewed Nov 10, 2026