skip to content

When do you accept a hidden-quadratic call inside a loop instead of paying for the rewrite?

level: principalimportance: should knowfreq 35%

answer

  1. bounded by luck, or by validation?
  2. measure at ten times today's volume
  3. request path or overnight batch?
  4. does the fix add a structure to maintain?
  5. make the assumption fail loudly later

basics

~20 s

Accept it when an enforced bound caps the input, the measured cost at ten times today's volume still fits the budget, and the simpler code is clearer. Then encode that bound as validation plus a test that fails when it is exceeded.

solid answer

~50 s

Asymptotics rank algorithms; they do not by themselves decide code. I ask four things. Is the input bounded by something actually enforced — a validated limit, a page size — or only by today's luck? What does it measure at current volume and at ten times it? Where does it sit: a request path with a latency budget, or a nightly job with hours of headroom? And what does the fix cost the team, especially if it adds an auxiliary structure someone must keep consistent with the source? If the bound is enforced and the ten-times number is comfortable, the quadratic version can be the right code. What is never acceptable is leaving the assumption implicit: encode it as a limit or assertion, add a test that fails when the input outgrows it, and record the reasoning.

go deeper

for a junior

Know that a quadratic is not automatically a bug: what matters is how large the input can get. Be ready to ask what bounds the input rather than to assert that the code must be rewritten.

for a middle

Be able to turn the growth claim into numbers — cost at today's volume and at ten times it — and to say whether the assumed size is enforced by validation or merely observed in current data.

for a senior

Show the operating angle: which path this runs on and against what budget, how you would prove the regression, and what guardrail — an enforced cap, a test at the bound — you leave so the decision fails loudly if the assumption expires.

for a principal

Own the policy and the spend. Argue when the simpler quadratic is the right code, when an auxiliary structure's consistency risk outweighs its speed, how untrusted input changes the bar entirely, and what review and load-test habits keep these out without turning every review into complexity theatre.

## Why this is a judgement call at all Every hidden-cost finding in a review arrives with an implied verdict: it is quadratic, therefore fix it. That reflex is mostly right and occasionally expensive. Big-O is an **upper bound on growth**, not a statement about the wall-clock cost of the input you actually have, and a lead's job is to convert a growth fact into a decision under real constraints: a latency budget, a batch window, a team's maintenance capacity, and a backlog with other things in it. ## The five questions that decide it **1. Is the input bounded, and by what?** There is a large difference between "today it is about 200 items" and "validation rejects more than 200 items". The first is an observation that will expire; the second is a contract that a test can defend. Ask who can make the number grow — a customer uploading a bigger file, a new caller reusing this path in bulk, a merger doubling the account list. If the answer is anyone outside your team, treat the input as unbounded. **2. What is the measured cost now and at ten times?** Not the asymptote, the number. A quadratic pass over a few hundred items is microseconds. The ten-times run is the one that matters, because that is where a quadratic separates from a linear alternative by a factor of a hundred. If ten times today still fits inside the budget with room, the growth curve is not yet your problem. **3. Where does the code sit?** A synchronous request path shares its budget with everything else in the request and fails loudly and publicly. An offline job with a wide window absorbs a lot. The same line deserves different verdicts in the two places, and saying so is what distinguishes judgement from rule-following. **4. What does the fix cost the team?** Sometimes the fix is *smaller* code — hoisting a loop-invariant call, replacing a scan with a set lookup — and then there is no trade to discuss; take it. The genuine dilemma appears when the fix adds an auxiliary structure that must be kept consistent with the primary data: a second index that can now go stale, an incremental aggregate with its own invalidation rules. That is permanent complexity paid for a speed you may not need, and the correctness risk of a de-synchronised index can exceed the cost of the slow path. **5. What is the blast radius if you are wrong?** A quadratic that gets slow is a bad afternoon. A quadratic in an unbounded path that becomes a resource exhaustion vector under attacker-controlled input is an incident. Anything whose size an untrusted caller controls stops being a performance conversation. ## What acceptance must include Accepting a known quadratic is defensible; accepting it *silently* is not, because it is not the decision that fails, it is the decision's invisibility a year later. Attach three things: - **An enforced bound.** Turn the assumption into validation or an assertion, so exceeding it fails as a clear error rather than as a mysterious timeout. - **A test at the bound.** A check that runs the path at the assumed maximum and fails when the maximum rises. This is what converts "fine for now" into something the build maintains for you. - **A recorded reason.** Not "this is O(n^2)" — the reader can see that — but the *bound and the budget*: what input size was assumed, what it measured, and what changes the answer. ## Making it a habit rather than a debate At team scale, the goal is fewer instances reaching review, not sharper arguments in review. Practical levers: a review prompt that asks what each call inside a loop costs and whether its input can grow; a performance test at a multiple of production volume for the paths that matter, so regressions surface as a failing build rather than a page; and a short shared vocabulary for the recurring shapes — a membership scan inside a loop, a superlinear call whose input never changes, a value materialised only to be read — so people recognise them by sight. ## The failure modes at both ends The over-eager end is complexity theatre: blocking a change over a quadratic on a hard-capped input, buying a fragile index with no measurement behind it, and spending review capital that the genuinely unbounded path next week now cannot get. The permissive end is the one that produces incidents: "it has always been fine" repeated until the input size changes owner. A defensible position names the bound, cites a measurement, and leaves behind something that will fail loudly when the assumption stops being true.

  • Where is the bar different when the input size is controlled by an untrusted caller?
    It stops being a performance question. An unbounded quadratic on caller-controlled input is a resource-exhaustion path: a modest payload can consume disproportionate processor time and starve everything sharing the process. There the fix is not optional, and it comes with a hard, enforced input limit rather than only a faster algorithm, because the limit is what caps the damage from the next hidden cost.
  • The proposed fix adds an auxiliary index that must stay consistent. How do you weigh that?
    Weigh the correctness risk against the measured need. A second structure introduces invalidation paths and a class of bug where the index disagrees with the source, which is harder to detect than slowness. If the ten-times measurement says the slow path fits the budget, keep the single source of truth; if it does not, the index is justified and the consistency rules deserve tests of their own.
  • How do you keep this from becoming a rule that every loop must be linear?
    Anchor review comments to input growth and a budget rather than to notation. Ask what makes the input larger and who controls that, then ask what the path measured at a multiple of production volume. A comment carrying both of those changes behaviour; a comment carrying only an asymptotic label costs the team attention it will need for the real cases.

saying these in an interview costs you the question

  • Blocks any quadratic regardless of enforced input bounds
  • Accepts it with no measurement, only intuition
  • Treats today's typical input size as a guarantee
  • Leaves the size assumption undocumented and untested
  • Adds a fragile secondary index before measuring the need

context