skip to content

In a web framework's middleware chain, what happens if an element never invokes the rest of the chain, and what if it invokes it twice?

level: middleimportance: must knowfreq 60%

answer

  1. exactly one call is the contract
  2. skipping removes the handler entirely
  3. empty result or open exchange
  4. twice means duplicated side effects
  5. one response hides the second pass

basics

~20 s

Skipping the call removes the handler and everything after it: the client gets a default answer or waits for a timeout. Invoking twice runs the whole remainder twice, duplicating side effects while the client still sees one response.

solid answer

~50 s

The contract is exactly one delegation per element, and both ways of breaking it are hard to see in code review. A branch that returns without delegating and without producing a response leaves the request unserved: depending on the framework, the client receives whatever default the pipeline synthesises for an empty result, or nothing at all until an idle timeout fires, and the handler simply never ran. Calling twice is worse, because it usually succeeds quietly: the handler and every inner element run a second time, so writes, charges and messages happen twice, while the second attempt to write a response hits one that is already produced and gets rejected or dropped. The client sees one response and the duplicate damage is invisible on the wire. The only honest defence is a test for every exit path of every element.

go deeper

for a junior

Know that nothing continues by itself: an element that returns without invoking the rest of the chain has ended the request there, handler included.

for a middle

Explain both failure modes and their symptoms, including why a duplicate pass shows up in data and metrics rather than in the response the client receives.

for a senior

Demonstrate the diagnosis: compare entry counts with handler invocations, recognise latency pinned at a client timeout, and require a per-branch test that asserts exactly one delegation.

for a principal

Own the invariant at the platform level. Decide whether pipeline elements are written freely by every team or supplied as reviewed building blocks, since one forgotten line in a shared element affects every endpoint.

## The contract An element in a wrapping chain is trusted with the whole remainder of the pipeline. Normal behaviour is **exactly one** delegation call, its result returned outward. The framework cannot enforce that, because delegation is an ordinary function call in code you wrote, so both deviations are yours to prevent. | | Missed delegation | Double delegation | |---|---|---| | Handler invocations for one request | zero | two | | What the client sees | a default or empty response, or a timeout | one response, normally the first | | Typical damage | the request is never served, resources stay held | duplicated side effects and doubled metrics | | Where it hides | a rarely taken branch that returns early | a branch that calls, then falls through to a shared call | | How you notice | an endpoint that silently does nothing | duplicate rows, duplicate messages, halved-looking success rates | ## When the call never happens Two situations look identical in the code and are completely different in intent. An element may return without delegating **because it has already produced the answer itself**, which is a legitimate move with its own rules. The bug is returning without delegating **and** without producing anything. What the caller experiences then depends on the execution model: - Frameworks that treat a finished chain with nothing written as a result in its own right synthesise something, often an empty success or a not-found style answer. The response is fast, wrong, and gives no hint that an element ate the request. - Frameworks that expect the response to be written before the exchange completes may simply keep it open. The connection, and in a thread-per-request model a worker, stay occupied until a client or server idle timeout fires. The tell is a latency graph pinned exactly at the timeout value. The usual causes are ordinary code shapes rather than exotic mistakes: 1. A guard that logs a suspicious request, was meant to continue, and returns instead. 2. A conditional where one branch delegates and the other silently falls off the end. 3. A refactoring that moved the delegation call into a helper which is not reached on every path. 4. In deferred-result models, calling the remainder but not returning or composing its result, so the outer elements believe the work is finished when it has barely started. ## When the call happens twice A second delegation re-enters everything inside this element, including the handler. The handler has no way to tell it is running for the second time, because from its point of view this is a normal invocation of a normal request. - **Side effects double.** Two rows, two charges, two outbound messages. This is the real damage, and it is the element's fault, not the handler's. - **The response usually rejects the duplicate.** The second pass tries to produce a response that has already been produced, so the framework raises an error or discards the extra output. Only one response reaches the client, which is why the wire gives no clue. - **A one-shot request body is empty the second time.** Request bodies are frequently streams that can be read once, so the replayed pass sees nothing and the handler answers with a confusing validation failure. - **Inner state is counted twice.** Metrics, rate-limit counters and acquired resources inside the chain all see two passes for one client request. The classic code shape is not a deliberate second call but a fall-through: a branch that delegates and then continues into a shared delegation statement at the end of the body. ## Is replaying the remainder ever legitimate? Rarely, and only with three guarantees in place: nothing has been written to the client yet, so the element must be buffering the response rather than letting it stream; the request body is replayable, which normally means it was captured in memory first; and the inner work is idempotent or the element knows exactly which side effects it is repeating. A middle position in a server-side chain can seldom promise all three, which is why retrying belongs at boundaries that can, such as an outbound client, rather than inside the request pipeline. ## Making both failures visible 1. **Test every branch of every element**, asserting that the inner callable was invoked exactly once, using a stub that counts invocations. This is the only check that catches a forgotten call on a rare path. 2. **Make the call the last statement** on each path, or structure the body so there is a single delegation point, so a fall-through cannot reach a second one. 3. **Compare counters**: requests entering the outermost element against handler invocations. A persistent gap means requests are being eaten; a persistent excess means something is replaying them. 4. **Watch for timeout-shaped latency.** A cluster of requests completing at exactly the client timeout is a strong hint that something stopped delegating on a particular path.

  • How do you tell a deliberate stop from a forgotten delegation call by looking at the code?
    Check whether the path produces a response before returning. A deliberate stop answers the request itself, usually setting a status and body on purpose; a forgotten call returns with nothing produced and nothing delegated, which is why the request ends up unserved.
  • Why is double delegation usually invisible in the response the client receives?
    Because the second pass cannot deliver a response. Only one answer can be written for one exchange, so the duplicate output is rejected or discarded while its side effects have already happened inside the handler. The evidence is in the data and the metrics, not on the wire.
  • What single test would you require for every element in a pipeline?
    Each branch invoked with a stubbed remainder that counts calls, asserting exactly one call on the normal paths and a produced response on any path that deliberately does not delegate. It is cheap, and it is the only way a rarely taken guard gets exercised before production does it.

saying these in an interview costs you the question

  • Assumes the framework continues the chain automatically
  • Thinks a skipped call always produces a clear error
  • Believes calling twice just wastes a little time
  • Blames the handler for duplicated writes
  • Treats a forgotten call and a deliberate stop as the same thing