A state change never appears on screen while other work re-runs producing identical output — how do you diagnose each?
answer
- two symptoms, one comparison
- measure identity and contents separately
- the four-way table
- late by one interaction means mutation
- repair the producer, not the seam
basics
~20 sCapture the value at the seam across two passes and compare identity and contents as separate facts. Same identity with different contents means an in-place write; new identity with identical contents means a producer allocating each pass. Repair the producer.
solid answer
~50 sBoth symptoms come from one comparison, so one measurement separates them. Hold the previous value at the seam and, on the next pass, ask two independent questions: did the **identity** change, and did the **contents** change? Same identity with changed contents is a write applied in place — the change is real and the comparison could not see it. New identity with unchanged contents is a producer building a fresh value every pass — nothing changed and the comparison had to report one. The other two combinations are healthy. Fingerprints help before you instrument: a value appearing one interaction late points at the mutation case; one consumer frozen while others update points at a value copied out of a tracking channel; work repeating with identical output points at churn. Fix the producer, not the seam.
go deeper
Learn the two shapes: nothing happened when it should have, or everything happens when it need not. Both are about the value handed to the seam, not about the screen.
Be able to run the measurement: hold the previous value, then ask about identity and contents separately, and read the four-way table to name the cause.
Bring the fingerprints — one interaction late, one frozen consumer, repeating identical work — and repair at the producer, explaining why forcing passes or deepening comparisons only hides the cause.
Set up the codebase so the diagnosis is rarely needed: one place where writes happen, shapes that keep copy paths short, and review habits that catch an in-place write and an inline value handed to a comparing seam.
## Two symptoms, one comparison A seam compares the value it has now with the value it had before and decides whether dependent work runs. Exactly two things can go wrong there, and they are mirror images: - **a change nobody saw** — the data changed but the comparison reported "same", so the screen keeps the old value; - **work nobody needed** — the data did not change but the comparison reported "different", so work re-ran and produced the output it produced before. Because they share a mechanism, they share a diagnostic. Stop treating them as "a rendering bug" and "a performance bug" and treat both as a question about one value at one seam. ## The measurement Keep the previous value at the seam — a reference held across passes is enough — and on the next pass record two facts separately: 1. is the current value the **same object** as the previous one? 2. do the current and previous **contents** differ? | identity | contents | what it means | where to fix it | |---|---|---|---| | same | same | healthy skip — nothing happened | nothing | | same | differ | a write applied in place to the value already held | the write site: produce a new value, or write through the tracking channel | | differ | differ | a healthy update | nothing | | differ | same | a producer allocating a fresh value each pass | the producer: hoist, pass primitives, or build only on real input change | The table is the whole technique. Most time lost to these bugs is lost by collapsing the two questions into one — "is it equal?" — which is exactly the question the broken comparison already answered. ## Fingerprints that shortcut the measurement - **The value appears one interaction late.** Classic in-place write: a later, unrelated pass re-reads the mutated object and paints the new data, so the update looks delayed rather than lost. - **Part of the screen updates and part does not, from one change.** A path copy with one level mutated, or a branch held from an older snapshot. - **One consumer is frozen while others update.** In a tracking runtime, that consumer copied the value out of the handle, or read it where no tracked computation was running. - **Work repeats on every pass with identical output.** Identity churn from the producer: an inline object or function, or a value rebuilt by mapping or parsing each pass. - **A repeating request or a spinning page.** Churn plus a write: the work writes state, the write starts a pass, the pass rebuilds the value the work depends on. - **Nothing reproduces in isolation but it misbehaves in the tree.** Suspect a shared object mutated by a second consumer, which makes the first consumer's snapshot untrustworthy. ## Fix at the producer, not at the seam The seam is where you *observe* the defect, and it is the wrong place to repair it in both directions: - for a missed change, forcing extra passes or bypassing the comparison hides the mutation, leaves the previous snapshot corrupted, and breaks anything that legitimately relies on unchanged branches keeping their identity; - for needless work, deepening the comparison converts a one-check seam into a data-sized walk on every pass, and leaves every *other* identity-keyed consumer still churning. Both repairs belong where the value is produced: build a new value for a real change, and hand back the same value when there is none. ## Guardrails so the diagnosis is rare - concentrate writes in one place — a state layer or an update function — so "how does a change get made here?" has one answer to review; - prefer shapes that make path copying short, and prefer primitives across boundaries where a seam will compare; - in review, treat two lines as worth a second look: a write to a property of something that came from state, and an object or function literal handed to something that compares its inputs; - keep the development-time warnings that flag repeated work and suspicious writes switched on, and watch for the loop shape early, because it is the one symptom users feel immediately. ## What differs by reactivity model The table travels; the second column's causes do not, entirely. Where the runtime compares snapshots, the missed change is almost always an in-place write and the needless work is almost always an inline value. Where reads and writes are intercepted, the missed change is more often a value that left the channel or a read taken outside a tracked scope, and needless work is rarer because dependencies are per-read rather than per-pass. Where a compiler instruments assignments, the missed change is typically a write the compiler could not see. Ask first which channel a change travels through in the runtime in front of you, then measure identity and contents separately — that is the part of the method that never changes.
- Why is "the update is lost" usually the wrong description of a missed change?Because the write normally succeeded — the data is in memory. What failed is visibility: the comparison at the seam could not see it. Saying "lost" sends people to the handler and the network response, when the defect is at the moment the change was applied to a value the runtime already held.
- Why not fix needless work by comparing contents at the seam?It works locally and moves the cost onto every check, where it scales with the data, while the fresh object still churns at every other identity-keyed seam. Stabilising the producer fixes the class; deepening one comparison fixes one instance and hides the cause.
- Which of the two bugs would you prioritise in production, and why?The missed change, and the loop variant of the needless one. A missed change shows users wrong data, and a loop burns the device and the network. Plain needless work with identical output is real waste but correct output, so it is measured and scheduled rather than hotfixed.
saying these in an interview costs you the question
- Calls a missed update a lost write and hunts in the network layer.
- Forces extra passes to make a mutated value appear.
- Deepens the comparison at the seam instead of stabilising the producer.
- Compares identity and contents as one question and learns nothing.
- Treats repeated identical work as harmless without checking for a write loop.