Skip to content

CONCEPT Cited by 1 source

Write-dependency graph

A write-dependency graph is a server-side (or client-side) data structure that tracks, for every node in an editable document, which other nodes are affected when this node is edited — even if those affected nodes do not directly reference the edited node in the data model.

It is the bidirectional, write-inclusive extension of a classical read-dependency graph. Read-only consumers (viewers, prototypes, read replicas) only need "which nodes do I depend on to render N?" — a directed graph of explicit foreign-key references. Editors have a stricter requirement: every mutation must propagate to every node it can derivedly affect, or else derived state drifts from source state silently.

(Primary source: sources/2024-05-22-figma-dynamic-page-loading — Figma's QueryGraph: bidirectional read + write dep graph over file nodes, held in memory by Multiplayer.)

The two edge classes

Explicit (foreign-key) edges. The dependent node carries a field naming the dependency. Figma's example: an instance node's componentID points to its backing component. Cheap to enumerate — just walk the node's fields.

Implicit edges. No field on either node names the edge; the dependency is structural. Figma's example: auto layout. When a node in an auto-layout frame is edited, the frame and its sibling nodes may relayout. None of them reference the edited node by FK. Edits to frame constraints, sizing, position, and child-order all bleed to their neighbors through layout rules.

Implicit edges are what make the write-dep graph non-trivial. They must be enumerated exhaustively by the engineers who own the data model, encoded as a distinct edge type in the graph, and kept in sync with code changes that add new relayout/derivation rules.

Why bidirectional

For dynamic loading, you need both directions efficiently:

  • Outgoing (dependencies): "To render / edit node N correctly, what else must I have loaded?" — drives the load-subset closure.
  • Incoming (dependents): "If node N changes, which other nodes derived from it must be updated?" — drives edit propagation and the recomputation of subscription sets.

Figma's post makes this explicit: "it was important that we could quickly determine both sets of dependencies for a given node." Bidirectionality makes both operations index-lookup cheap rather than full-scan.

Editing parity as the correctness bar

The write-dep graph's correctness contract is editing parity:

An edit made in a dynamically loaded file must produce exactly the same set of changes as if the file were fully loaded.

This is a completeness bar, not an approximation bar. A missing write dep presents as silent divergence — Figma's examples are instances drifting from components, broken layout, missing fonts. Because the failure mode is silent, the graph must be validated before dynamic-load becomes the live path. Figma did this via patterns/shadow-validation-dependency-graph, running the graph computation alongside the full-load path for an extended period and reporting any edits to nodes outside the computed write-dep set. That surfaced a cross-page recursive write-dep involving frame constraints + instances that the initial enumeration had missed.

What the graph enables

  • Subset computation on load. load_subset(page P) = closure(P, read_deps ∪ write_deps). Everything outside stays on the server. (concepts/reachability-based-subscription)
  • Selective edit fan-out. Ship an edit on node N to session S iff N is reachable from S's subscribed subset. Everything else is filtered server-side.
  • Subscription adjustment on edge mutation. If one user mutates a dep edge (e.g. swaps an instance's backing component), every other session whose subscription set changes as a result needs the newly-reachable nodes shipped.

Cost surface

  • Server memory — the graph lives alongside the full file in Multiplayer RAM. Figma does not size this publicly.
  • Graph-maintenance code discipline — every feature that adds a new derivation rule (a new kind of auto layout, new constraint type, new component override) must also add the corresponding edge type and validation test. This is an ongoing tax, not a one-shot cost.
  • Shadow-mode runtime — non-trivial effort for an extended period; required to exit the "maybe-incomplete" risk before flipping the live path.

Seen in

  • sources/2024-05-22-figma-dynamic-page-loading — canonical engineering-blog treatment of the concept, including the read→read+write extension, the bidirectionality requirement, the implicit-edge discovery problem, the editing-parity contract, and shadow-mode validation.
Last updated · 200 distilled / 1,178 read