RxJS Anti-Patterns: Nested Subscribes & Leak Factories RxJS is supposed to replace callback pyramids with declarative composition. Used well, that's exactly what happens. Used poorly, it becomes a callback pyramid with extra syntax — a inside a , wearing the vocabulary of reactive programming while doing none of the things that make it worth using. This is the single most common way real codebases misuse RxJS, and it's where most of the memory leaks and untraceable bugs in Angular apps are actually born. Why this isn't a style nitpick Consider a component that loads a user, then needs that user's orders, then needs the shipping status of the most recent order — three dependent async steps. This works. It also has three separate problems stacked on top of each other: 1. No unified teardown. Three independent subscriptions exist, and if the component is destroyed while the outer one is still waiting on the network, the two inner subscriptions were never created — but if it's destroyed while the middle one is pending, that subscription lives on with nothing to cancel it, quietly holding a reference until it resolves. 2. No error propagation. If fails, that error is trapped inside the outer subscribe's callback with no anywhere in sight — it either throws inside a callback RxJS doesn't expect to throw, or silently does nothing, and simply never updates. Nobody watching the outer subscription would ever know something failed. 3. Untestable in isolation. Testing this requires mocking three services and threading fake data through three levels of nested callbacks just to reach the assertion that actually matters. The fix: flatten with a mapping operator The chapter's previous lesson covered as the operator for cancel-and-replace. It has a second job here: flattening a dependent async step so it lives in the pipe instead of a nested callback. Same three dependent calls. One subscription, one teardown point, one place an error can be caught before it reaches the subscriber. If the user navigates away mid-chain, unsubscribing the single outer subscription cancels the entire pipeline — there's no orphaned middle subscription still waiting on a network call nobody cares about anymore. (A note on which flattening operator: cancels the previous inner subscription when a new outer value arrives — right for dependent chains like this one. runs all inner subscriptions concurrently — right when order doesn't matter and none should cancel the others. queues them strictly in order — right when a later step must not start before an earlier one finishes, like sequential writes.) Automatic teardown, not manual bookkeeping The second half of this anti-pattern is subscriptions that are never explicitly torn down at all — leak factories that don't even look like nested subscribes: Two fixes, both better than a manually tracked object you remember to in : Both remove the human step that's easiest to forget: remembering to tear something down. The principle that transfers Every framework has its own version of "subscribing inside a subscription." In React, it's a that calls , which triggers a second watching that state, which calls another — a chain of effects reacting to each other instead of one clear data flow, with the exact same symptoms: hard to trace, easy to leave a subscription or listener uncancelled, and a failure in the middle silently breaking the chain instead of surfacing where you'd look for it. The fix transfers too: flatten dependent async steps into one composed operation instead of nesting reactive callbacks, and let the framework's cleanup mechanism — the async pipe, , a cleanup function — do the teardown instead of a manually tracked reference. The Trade-off: Is Flattening Worth Restructuring the Code? A single nested subscribe is genuinely less code to write than pausing to pick the right flattening operator. The cost of not flattening only shows up once a second dependent step, or a real teardown requirement, enters the picture. The pain, concretely v1: One async step, one . No dependent calls yet, nothing nested. Perfectly fine as-is. v2: A second, dependent call is needed. The fastest edit is subscribing inside the first callback — it's one line, and the alternative means learning an operator. Ships today. v3: A third dependent call is added the same way. The pyramid is now three levels deep, and an early return path — the user cancels a dialog partway through — never explicitly stops the still-pending middle subscription. v4: The bug shows up in production only under a slow network, when a user navigates away mid-chain: a subscription no one remembers creating updates a component that's already been destroyed, and the observability team spends an afternoon chasing an error with no clear origin. Side-by-side Nested subscribes Flattened with a mapping operator --- --- --- Teardown when the component is destroyed mid-chain Each nested subscription needs its own cancellation; easy to miss the middle one One oute…