Step 4 — Perspective: What Any of This Is Worth

Goal

Price the previous three steps against the operations frontend code actually performs. This is the module. If you do one step, do this one.

Predict first

Rank by cost, and estimate each in nanoseconds:

  • one megamorphic property read
  • createElement + textContent + appendChild
  • addEventListener + removeEventListener
  • one style write + getBoundingClientRect (forced layout)
  • JSON.parse of a ~200 KB API response

Then answer: how many megamorphic property reads equal one forced layout? Write a number.

Run

npm run perspective

Expected output:

  operation                                          |        ns each
  monomorphic property read                          |            0.9
  megamorphic property read                          |            3.8
  createElement + textContent + appendChild          |          360.0
  addEventListener + removeEventListener             |          130.0
  style write + getBoundingClientRect (forced layout)|         4200.0
  JSON.parse of a ~200KB API response                |       396000.0

  The megamorphic penalty is 2.90 ns per property read.

  How many property reads must you de-megamorphise to save the cost of ONE:
    addEventListener + removeEventListener                     45 reads
    createElement + textContent + appendChild                 124 reads
    style write + getBoundingClientRect (forced layout)      1,448 reads
    JSON.parse of a ~200KB API response                   136,552 reads

What just happened

Five orders of magnitude separate the top and bottom of that table.

Removing one forced layout is worth de-megamorphising 1,448 property accesses. Avoiding one 200 KB JSON parse is worth 136,552. And fe-01 measured a layout-thrashing loop over 800 elements at 86 ms — that single fix is worth roughly 30 million de-megamorphised reads.

The arithmetic to reuse

Price the plausible-sounding proposal "fix megamorphic access in our render path":

penalty              2.9 ns per read
reads per render     50,000              (generous)
saving               0.145 ms per render
frame budget         16.7 ms
improvement          0.9%

Now price the alternative on the same path: removing the layout thrash, 86 ms. Same engineer, same week, ~590× the return.

The general form

Convert the ratio to an absolute per-operation cost. Multiply by the real operation count. Compare against the other items on the same critical path, in the same units.

This is not a fact about shapes. It is the method for every optimisation debate you will arbitrate — library choices, bundle-size claims, cache hit rates, re-render counts. Most proposals do not survive it, including your own, which is the point.

The honest counterweight

These effects are real, and there are situations where they dominate — see docs/analysis.md "When this actually matters". The failure mode is not writing shape-unstable code; it is a senior engineer spending a sprint on shapes while a request waterfall and a 200 KB main-thread JSON parse sit untouched on the same path.

Checkpoint

docs/verification.md Checkpoints 4 and 5.

Deliverable

Take one real code path you own. Price it with this method:

  1. What is the operation count?
  2. What is the absolute per-operation saving?
  3. What else is on the same critical path, in the same units?

A decision not to optimise, backed by that arithmetic, is a passing deliverable — and is the outcome most of the time.