skip to content

In a product-page stream that flattens in a failing recommendation source, what does moving recovery outside the flattening cost you?

level: seniorimportance: must knowfreq 54%

answer

  1. two sequences, not one
  2. the flattening is the boundary
  3. where the inner failure escapes
  4. outer recovery replaces what remains
  5. scope follows the attachment point

basics

~20 s

Everything except the fallback. The inner failure escapes into the outer sequence and terminates it, so one recovery step outside the flattening replaces the whole remaining page — the price and stock work already done is abandoned and sources still in flight are cancelled.

solid answer

~40 s

Flattening joins an inner sequence into an outer one, and an inner failure does not stay inside: it escapes outward and ends the **outer** sequence. A recovery step placed after the flattening therefore only ever meets a failure that has already killed the page, so its fallback replaces the entire remainder — the recommendation block and the price and stock blocks alike — and any inner sources still running are cancelled with it. Attach the recovery to the inner recommendation source instead and it substitutes an empty list for that sub-result *before* it reaches the outer sequence. The outer sequence never sees a failure, the other blocks finish, and the page renders with one empty section. The rule is that recovery scope follows its attachment point.

code

pseudocode · 12 lines
pseudocode
// placement A - recovery outside the flattening
page = productIds
         .flattenEach(id => fetchRecommendations(id))
         .recoverWithValue(EMPTY_LIST)
// an inner failure escapes, ends the outer sequence, cancels
// the other in-flight fetches; ids after it are never processed

// placement B - recovery on the inner source
page = productIds
         .flattenEach(id => fetchRecommendations(id)
                              .recoverWithValue(EMPTY_LIST))
// the failure is substituted before it can cross the boundary

go deeper

for a junior

Know that a pipeline can hold sequences inside a sequence, and that a failure in an inner one does not stay there by itself.

for a middle

Explain the boundary: recovery on the inner source substitutes for that sub-result, while recovery after the flattening only meets a failure that has already ended the outer sequence.

for a senior

Show the operational consequence — outer-only recovery abandons work that had already succeeded and cancels sources still in flight, turning one degraded block into a blank page.

for a principal

Own the rule for which blocks may degrade and which may not, and keep it visible in the code so nobody wraps a whole page in one recovery step for convenience.

## There are two sequences here, not one Assembling a product page means running a sub-request per block: a price lookup, a stock check, a recommendation fetch. Each of those is its own sequence — an **inner source** — and a flattening stage joins their values into the single **outer sequence** the subscriber consumes. That boundary is the whole subject. A failure raised by an inner source does not stay inside it. Unless something inside handles it first, it **escapes outward**: the flattening stage propagates it into the outer sequence, and the outer sequence terminates on it like any other failure. ## What each placement replaces | Recovery attached to | What the failure meets first | What the fallback replaces | What survives | |---|---|---|---| | The inner recommendation source | The inner recovery, before flattening | That one sub-result | The outer sequence, the other blocks, other in-flight sources | | The outer sequence, after flattening | The outer sequence, already terminated | The entire remainder of the page | Only values already delivered to the subscriber | The second row is the cost the question asks about, and it is larger than people expect: - **Work already done is abandoned.** The price lookup may have succeeded a moment earlier; its value is gone unless it had already been delivered. - **Work still running is cancelled.** Terminating the outer sequence releases its subscription, which propagates cancellation to every inner source still in flight. A stock check three quarters of the way through is stopped. - **Work not yet started never starts.** If the outer sequence was walking a list of products, the products after the failing one are never processed. - **One degraded block becomes a blank page.** The subscriber gets one fallback where it expected a composed page. ## Why the mistake is so easy to make A single recovery step at the bottom of the chain reads like a safety net, and in a flat pipeline it very nearly is one. The moment the pipeline contains inner sources, that reading is wrong: the outer recovery is not protecting the inner work, it is reacting to the inner work having already destroyed the outer sequence. The net is below the floor it was supposed to catch you on. The corrected mental model is one sentence: **a recovery step's scope is whatever sequence it is attached to.** Attached inside, it scopes to the sub-result. Attached outside, it scopes to the page. ## Choosing per block, not per pipeline This is what turns the mechanism into a design decision. The three blocks of a product page do not have equal rights to degrade: 1. The **recommendation** block may become an empty list. A page with no recommendations is a worse page, not a wrong one, so an inner recovery here is correct. 2. The **stock** block may sometimes degrade to an unknown state, provided the page can render that honestly rather than as 'in stock'. 3. The **price** block may not be substituted at all. A page that shows an invented price is wrong in a way that no rendering can rescue, so the price's failure should be allowed to end the page — or be translated into a domain failure the caller renders as an error. A single outer recovery step cannot express any of this, because it cannot tell which block failed by the time it sees the failure. Placing recovery per inner source is what makes the policy visible in the code: the blocks that carry a recovery step are exactly the blocks that are allowed to degrade. ## Concurrency sharpens it further If the flattening runs several inner sources concurrently, the difference in blast radius grows. With inner recovery, one failing recommendation fetch substitutes its empty list and its siblings continue untouched. With outer-only recovery, the first inner failure terminates the outer sequence and every sibling still running is cancelled — so a single flaky block can consume the results of several healthy ones, and the more concurrency you added for latency, the more work you throw away. ## Answering it in an interview Name the boundary, state the escape direction, then the cost. 'The inner failure escapes into the outer sequence and terminates it; recovery after the flattening can only replace the whole remainder, cancelling siblings and abandoning finished work. Attached to the inner source, the failure is substituted before it ever crosses, so the page loses one block instead of all of them.' That is the answer, and the follow-up will be which blocks you would allow to degrade at all.

  • If the price lookup must never be faked, what does that imply about where recovery may sit?
    Recovery may sit only around the blocks allowed to degrade. A chain-wide recovery step cannot tell which block failed, so it would substitute for a price failure too and ship an invented price. The price's failure should end the page, or be translated into a domain failure the caller renders explicitly.
  • What happens to sibling inner sources still running when the failure is handled only outside the flattening?
    They are cancelled. Terminating the outer sequence releases its subscription, and that cancellation propagates to every inner source still in flight. The more concurrency the flattening was given, the more nearly-finished work one failing block discards.
  • Does adding a second recovery step outside the flattening fix an unhandled inner failure?
    No. It changes what the subscriber receives after the outer sequence has already been terminated, but not the fact that it was terminated. The page is still replaced wholesale, siblings are still cancelled — only an inner recovery prevents the failure from crossing the boundary.

saying these in an interview costs you the question

  • Thinks one recovery step at the end protects every inner source.
  • Believes an inner failure stays contained without a recovery step inside.
  • Says the outer sequence continues with the next product after an unhandled inner failure.
  • Assumes placement changes only which value is substituted, not how much is lost.
  • Treats the fallback as replacing just the failed sub-result wherever recovery sits.