skip to content

On a memory-capped device, when a fixed-capacity stack fills, do you reject the push or grow the stack?

level: principalimportance: nice to knowfreq 28%

answer

  1. what is fixed on this device, and what is not
  2. where do you want the failure to happen
  3. growth defers the failure somewhere worse
  4. worst case must be sized and proven up front
  5. make every drop visible in telemetry

basics

~20 s

On memory-capped hardware, reject the push and make the rejection visible. A fixed capacity turns an unpredictable out-of-memory failure into a local, testable policy — provided the caller is told and someone has decided which readings may be lost.

solid answer

~50 s

The real choice is not reject versus grow; it is where the failure happens and who finds out. A fixed capacity puts it at the push, in one line, at a moment you can reproduce in test and count in telemetry. Growth moves the same failure to an arbitrary later allocation, in whichever subsystem asked for memory next, on whichever device had the deepest buffer that day — and it gives up the bounded worst-case per-push cost a fixed-rate sampling loop may depend on. So I bound the buffer, size it against the worst backlog I intend to survive, and spend the design effort on the *drop policy*: reject the newest or discard the oldest, whichever loses less, and increment a counter that reaches the fleet dashboard. Growth is defensible only when the bound cannot be estimated — and even then it needs an explicit ceiling with defined behaviour there.

go deeper

for a junior

Know that a fixed-capacity stack has a full state and that a push must report it rather than write past the end. Recognising the overflow guard in code is what is expected here.

for a middle

Explain what each option gives up: a bounded buffer loses data at the ceiling, while a growing one gives up predictable memory and a bounded worst-case push cost. Name both sides rather than picking a favourite.

for a senior

Argue a concrete policy for a real constraint — which end loses data and why — and insist that the drop be counted and reported so the sizing decision can be revisited with evidence.

for a principal

Own the decision as a fleet-wide contract: the capacity, the drop policy and the telemetry belong in the documented design, and a dozen independently growing buffers is an unbudgeted memory ceiling nobody owns.

## Reframe the question before answering it "Reject or grow" sounds like a data-structure question and is really an operations question: *every design here loses something, and you are choosing what to lose and who is told.* A candidate who answers with a preference has not shown judgment; one who answers with the decision procedure has. The setting: a sensor device with a hard memory budget buffers unacknowledged readings in a stack, holding them until the uplink confirms receipt. Backlogs happen when the link is down. The stack is what stands between a temporary outage and lost data. ## What a fixed capacity actually buys 1. **A memory number you can prove before shipping.** Capacity times element width is the entire footprint. It can be reviewed, budgeted against every other subsystem on the device, and verified in test. A growing buffer's footprint is a function of field conditions you do not control. 2. **A failure at the point of cause.** The full condition is detected at the push, in one line, with the whole context present. Contrast the growth path: the memory pressure is created here and the failure surfaces wherever the next allocation happens — possibly in an unrelated subsystem, possibly minutes later, possibly as a device reset with no useful evidence. 3. **A bounded worst case per push.** Every push costs the same. A stack that acquires more room when full has, by construction, at least one push that costs much more than the others, and a fixed-rate sampling loop with a real deadline cannot always absorb that. 4. **A testable failure.** You can fill a bounded stack in a test and assert on the behaviour. "What happens when memory runs out" is far harder to provoke faithfully. What it costs is blunt: past the ceiling, data is lost. That cost is the thing to design, not to hide. ## Designing the drop policy Once capacity is bounded, three policies are on the table, and the domain decides between them. - **Reject the newest.** The buffer keeps the oldest backlog. Right when the readings form a sequence whose beginning matters — an event trace, an incident window you must not truncate. - **Discard the oldest to make room.** The buffer keeps the most recent history. Right when freshness dominates — current state, recent telemetry, where a stale reading has no value. Note that this is no longer a pure stack contract; you have chosen a bounded buffer with an eviction rule, and that should be stated in the type's contract rather than smuggled into the push. - **Apply backpressure.** Do not drop at all: slow or pause the producer, downsample, or switch to a coarser sampling rate while the backlog persists. Often the best answer where the producer is under your control, because it degrades resolution instead of losing whole readings. The non-answer is *drop silently and return success*. Silent loss is discovered months later by someone reconciling counts, and by then there is no way to tell which devices lost what. ## The observability requirement Whichever policy you pick, the drop must be counted and the counter must leave the device. A per-device dropped-readings counter turns an invisible correctness problem into a capacity signal: if the fleet's drop rate is zero the buffer is oversized and you can reclaim memory; if a subset of devices drops constantly you have found either an undersized buffer or an uplink problem, and you can tell them apart by whether drops correlate with connectivity. Without the counter you have neither piece of information, and every future sizing argument is guesswork. ## When growth is genuinely the right call Growth wins when the bound cannot honestly be estimated and the cost of losing data exceeds the cost of an unpredictable stall — a diagnostic capture during a field investigation, say, where a truncated trace is worthless. Even then, treat unbounded growth as an unowned decision: set an explicit hard ceiling and define the behaviour at that ceiling, because "grow" with no limit is just "reject" with the rejection deferred to an allocator you do not control and cannot reason about. ## The organisational half This decision outlives the code. Capacity, drop policy and the counter belong in the device's documented contract, because the person tuning the buffer in two years will be a different person, on a different hardware revision, with a different memory budget. Committing the policy to a shared type also stops each subsystem from inventing its own: on a constrained device, a dozen independently growing buffers is not a set of local choices, it is an unbudgeted, fleet-wide memory ceiling that nobody owns. The principal-level answer is to make the bound explicit, the loss visible, and the sizing revisitable — not to pick a side.

  • If you reject the push, what does the sampling loop do with the reading it could not store?
    That is the decision the rejection forces into the open, which is its value. The options are to drop it and increment a counter, to downsample so fewer readings compete for the buffer, or to apply backpressure by slowing the producer while the backlog persists. Any of the three is defensible; what is not defensible is a push that reports success while discarding the reading, because that removes the choice from everyone downstream.
  • When does growing win, even on constrained hardware?
    When the depth genuinely cannot be estimated and truncated data is worthless — a diagnostic capture during a field investigation, for example. Even then, growth needs an explicit hard ceiling and a defined behaviour at that ceiling, because unbounded growth is not a policy, it is the same rejection handed to an allocator you cannot reason about, at a moment you cannot predict.
  • How would you size the capacity in the first place?
    From the worst backlog you actually intend to survive: the longest uplink outage you commit to covering, times the sampling rate, times the element width, plus margin for the sampling jitter. Then ship the drop counter and let the fleet correct the estimate — sustained zero drops means the buffer is oversized and memory can be reclaimed, while a drop rate correlated with connectivity means the outage assumption was too optimistic.

A bounded buffer is a lifeboat with a stated capacity: everyone knows the limit and plans for it. An unbounded one is a boat that keeps taking passengers until it quietly sits too low, and you find out in weather.

saying these in an interview costs you the question

  • Says always grow, memory is cheap
  • Treats silent dropping as acceptable if pushes still succeed
  • Ignores that growth moves the failure to an unrelated allocation
  • Never mentions counting or reporting dropped readings
  • Assumes a fixed capacity needs no sizing rationale
  • Calls unbounded growth a policy rather than a deferred failure

context