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+appendChildaddEventListener+removeEventListener- one style write +
getBoundingClientRect(forced layout) JSON.parseof 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:
- What is the operation count?
- What is the absolute per-operation saving?
- 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.