The PM wants a portfolio dashboard shipped by Friday. A junior builds it fast — two nested .map() calls. The outer loop walks asset classes (Equity, Fixed Income). The inner loop walks tickers inside each asset class. Twenty-five lines of readable JSX. Ships Tuesday. QA greenlights it.

Wednesday, product reviews the roadmap. “Actually, we need sectors in the middle. Equity contains Tech which contains AAPL, MSFT, GOOGL. The tree has to grow by one depth.” Thursday morning the junior reopens the file. The outer map still yields asset classes. The inner map — which used to yield tickers — now yields SECTORS. Sectors have children, not a .value. And ticker.value at depth 2 becomes undefined for every sector.

Live render · two nested .map() calls
Equitydepth 1
AAPL$45,000
MSFT$32,000
Fixed Incomedepth 1
US 10Y$25,000

Depth 2 works. Two levels of data match two levels of JSX.

The flat version · shipped Tuesday
1
// The flat render every first-pass ships. Hard-coded two levels.
2
function FlatPortfolio({ portfolio }) {
3
  return (
4
    <div>
5
      {portfolio.children.map((assetClass) => (
6
        <div key={assetClass.id}>
7
          <div>{assetClass.label}</div>
8
          <div style={{ paddingLeft: 20 }}>
9
            {assetClass.children.map((ticker) => (
10
              <div key={ticker.id}>
11
                {ticker.label}: ${ticker.value}
12
              </div>
13
            ))}
14
          </div>
15
        </div>
16
      ))}
17
    </div>
18
  );
19
}
20
21
// What breaks when product adds a sector level
22
// (asset class → sector → ticker)?
23
//   1. The inner map() now yields objects with children, not leaves.
24
//     ticker.value is undefined; display shows "Tech: $undefined".
25
//   2. Copy-paste the outer block one level in. Now two render blocks
26
//      exist; any bug fix in one has to be ported to both.
27
//   3. Every new level doubles the JSX surface area. At four levels,
28
//      the component is unreadable — bugs hide in the mismatch
29
//      between copy-paste twins.
30
//   4. Removing a level requires the reverse cascade of deletions.
31
//      No shape is stable.
Step 1 / 3Probe: what happens when depth 3 arrives?

Toggle the “depth 3” switch in the hero above. The flat render is exactly the shipped Tuesday code — two nested .map() calls, hard-coded for two levels. Watch how every sector row renders once the third depth is present. Before we gate the prediction, make sure you see the broken output at least once.

probe
break
cost
residue