After one state write, what work does a re-run-and-diff runtime do that a fine-grained tracking runtime avoids?
answer
- same write, two discovery units
- component function versus one computation
- subtree size versus dependent count
- bookkeeping on reads is the fine-grained bill
basics
~20 sA re-run-and-diff runtime re-executes the owning component function and produces a whole fresh description of its output, then compares it. A fine-grained runtime walks the subscriptions recorded for that one value and re-runs only those computations.
solid answer
~40 sIn a re-run-and-diff runtime the setter marks the component stale, and the component's function runs again in full: every expression in it is re-evaluated and a new description of its output is built, then handed on to be compared. Work therefore scales with the size of the re-run subtree and its output, whether or not much differs. In a fine-grained runtime the write consults the edges recorded when computations read that value, and re-runs just those - so work scales with the number of dependents, and a single text binding can update without its component function running again. The trade is bookkeeping: the fine-grained runtime pays on every read and maintains a dependency graph; the re-run runtime pays in re-execution but keeps a simpler mental model, where output is a plain function of inputs.
go deeper
Hold on to the two units: one model re-runs the component's whole function, the other re-runs only the small expressions that read the changed value. That contrast alone answers the screening version.
Explain both cost drivers - re-run subtree and output size against dependent count - and name what each model pays for it: re-execution plus a scheduler, or a per-read edge plus a live graph.
Say where the difference has actually bitten: long lists, wide trees, high-frequency writes, and codebases carrying heavy manual caching to claw back work the re-run model does by default.
Treat it as a cost the whole team pays per feature. Precision reduces wasted work; a function-shaped model is easier to read and to onboard into. Decide which cost your codebase can better absorb.
## The same write, two runtimes Take one write: a counter held by a component goes from 4 to 5, and exactly one piece of text on screen shows it. The two dominant models reach the same screen by very different routes, and the difference is entirely in the *discovery* step - what the runtime learns and therefore what it marks. ## What a re-run-and-diff runtime does 1. The setter is called with a new value. It stores it and records that this component is out of date. 2. A scheduler decides when to act, then invokes the component's function again. 3. The function runs **in full**: local variables recomputed, derived values recomputed unless explicitly cached, child elements described again, and a fresh tree-shaped description returned. 4. That description is handed to the next stage, which works out what the change means for the host tree. So the unit of re-execution is the **component function**, and by default a component's re-run implies describing its children too. The cost driver is the size of the re-run subtree and the amount of output it produces - not the size of the actual difference. One character of text changing can still mean re-executing a function that builds hundreds of nodes' worth of description. ## What a fine-grained runtime does 1. The write goes through the value's handle. 2. The handle holds a list of dependents - computations that read it while they were running. 3. Each dependent is marked; when they run, each re-executes **only itself**. The unit of re-execution is the **computation**, which can be as small as one text binding or one attribute expression. A component's setup function typically runs once, at creation, rather than once per update; the parts that re-run are the reactive expressions inside it. Cost scales with dependent count, so a value read in one place costs one re-run however deep the tree is. ## Where the cost actually goes | | Re-run and diff | Fine-grained tracking | |---|---|---| | Unit re-executed | component function | single computation | | Work after one write | proportional to re-run subtree and output | proportional to dependents | | Per-read cost | none | records a dependency edge | | Standing memory | previous output description | dependency graph | | Cost of a deep tree | grows with what is re-run | unchanged | | Mental model | output is a function of inputs, recomputed | a graph of values and reactions | Neither column is free. Re-running pays in wasted re-execution and needs a scheduler, and its users spend real effort telling it to skip work or to cache a derivation. Fine-grained tracking pays a small cost on every read, keeps a graph alive, and has to define an order in which dependents run so that nothing observes a half-updated set of values. It also asks users to keep reads inside tracked scopes - the edge exists only because the read happened where the runtime could see it. ## Why the comparison is not simply "fine-grained is faster" At typical application sizes both are comfortably fast, and the measurable difference usually shows up in the worst case: large lists, wide trees, high-frequency writes such as a drag or an animated value. Re-running also has a genuine simplicity dividend. If output is a function of inputs and the whole function re-runs, then reading the code top to bottom tells you what will be on screen; in a graph model, you reason about which expressions re-ran and in what order, which is a different and sometimes harder question to answer from the source alone. There is also a hybrid direction worth naming: a re-run runtime narrows work with caching and bailouts, while a fine-grained runtime has no need for them because the narrow unit is the default. That is why "how much manual memoisation does this codebase carry?" is often a better question than a benchmark. ## The boundary to keep straight Everything above stops at "this is marked to re-run". What the marked work then does to the host tree - whether an existing node is patched or a new one replaces it - and when the marked work is flushed relative to events belong to later stages. In an interview it pays to state that explicitly, because it shows you are not conflating three different mechanisms under the word "re-render". The answer to this question is about **how much has to be re-executed to find out what changed**, which is decided entirely by the discovery model. ## How to answer it out loud A compact version: "The re-run runtime learns only that a component is stale, so it re-executes that function and produces a full fresh description before anything is compared. The tracking runtime learns exactly which computations read the value, so it re-runs those and nothing else. The first pays in re-execution and buys simplicity; the second pays in per-read bookkeeping and buys precision."
- Why does a fine-grained runtime rarely need manual memoisation of derived values?Because its default unit is already narrow. A derived value is a computation with its own dependents, so it recomputes only when something it read changed, and only its own dependents react. A re-run runtime recomputes every expression in the function by default, so it offers caching as an opt-in to claw work back.
- Does a fine-grained runtime ever re-run a whole component?Yes - when the change affects structure rather than a value, such as a list gaining items or a conditional branch flipping, the computation that owns that region re-runs and recreates what is inside it. Fine-grained means the marked unit is small, not that structure never rebuilds.
- One runtime re-runs a function on every write. Why does it need a scheduler at all?Because many writes can happen in one event, and re-running per write would repeat work and could show intermediate states. A scheduler coalesces writes into one pass, and lets the runtime choose when to spend the re-execution - which is also what makes interruptible work possible.
saying these in an interview costs you the question
- Says fine-grained tracking is simply faster, with no cost named.
- Thinks a re-run means the whole application re-executes from the root.
- Believes a fine-grained runtime never re-executes anything component-sized.
- Confuses re-running the function with updating the host tree.
- Claims the dependency graph is built at build time from the source.