Skip to content

CONCEPT Cited by 1 source

Automatic dependency tracking

Automatic dependency tracking is the mechanism where a reactive framework records the dependency graph implicitly, by observing what each computation reads at runtime, instead of requiring developers to declare dependencies by hand. Every value read during a re-materialization / render / recompute is recorded as a dep of the consuming node; the framework uses that graph to push invalidations on future source changes.

Figma's framing (Source: sources/2026-04-21-figma-rebuilt-foundations-of-component-instances):

"Dependency tracking is automatic. As nodes read data during materialization, Materializer records those relationships implicitly. Developers don't declare dependencies by hand; the system learns them as it runs."

Why automatic, not manual

Hand-declared dep graphs have a well-known failure mode: they drift from the code's actual reads. A developer adds a new property access inside a blueprint; forgets the matching depends_on; the derived value is no longer invalidated on that source's change; failure mode is silent staleness. No crash, no error — the derived subtree just renders yesterday's value.

The same class of bug shows up in:

  • Manual React useEffect dep arrays (hence ESLint's react-hooks/exhaustive-deps).
  • Manual Redux selectors that forget to list a slice.
  • Hand-maintained write-dependency-graph edge enumerations — Figma's QueryGraph had to shadow-validate a hand-enumerated write-dep list for an extended period, which surfaced a missing cross-page constraint edge.

Automatic tracking removes the drift class entirely: if the code reads it, the framework knows. The dep graph is correct by construction.

Implementation shapes (not detailed in Figma's post)

Figma does not disclose the mechanism. The common implementations across reactive-framework literature:

  • Read hooks / accessor interception. Wrap data accesses so every node.getProperty(k) passes through a hook that records the (reader, source, property) tuple. MobX-style.
  • Proxies. Wrap source objects in Proxy so every get is observable. SolidJS-style.
  • Explicit tracking context. Maintain a thread-local "current reader" while a materialization runs; reads push into that context. Vue ref/computed-style.
  • Code-gen. Static analysis of blueprints emits explicit depends_on at compile time. Closer to pull-based + compile-time optimization.

Materializer's post says "records those relationships implicitly" — consistent with any of read-hook / proxy / tracking-context at runtime. The most likely shape given C++ + WebAssembly: tracking context maintained during blueprint execution, with property reads on document nodes explicitly routed through a tracked accessor.

Tradeoffs

Upside:

  • No drift. Graph always in sync with code.
  • Minimal blueprint API. Authors write straight-line code that reads what it needs; the reactivity layer is invisible.
  • Supports over-reads. Reading a field and then not using its value still registers a dep — conservative safety bias (false positives invalidate unnecessarily but never cause staleness).

Downside:

  • Runtime overhead. Every read pays a small tracking cost.
  • Dynamic reads are first-class. If a blueprint reads A in branch 1 and B in branch 2, the recorded deps will differ between runs — the dep set is itself a function of inputs. Tracking must accommodate.
  • Over-tracking risk. A blueprint that incidentally touches many fields records them all as deps; noisy invalidation. Practical systems often expose untracked(() => …) escape hatches.
  • Debuggability. Silent deps can surprise; "why did my node re-materialize?" is harder than with explicit depends_on. Inspection tooling is usually required.

Interaction with push-based invalidation

Automatic tracking is the refinement that makes push-based invalidation tractable at scale. Push-based requires a graph; manual graphs drift; automatic tracking produces a correct-by-construction graph. The combination — push-based + auto track — is the de facto modern reactive-runtime default (MobX, SolidJS, Vue, Svelte runes, Jotai, now Figma's Materializer at document-tree granularity).

Seen in

Last updated · 200 distilled / 1,178 read