PATTERN Cited by 1 source
Runtime orchestration toward unidirectional flow¶
A client runtime that has grown by accretion — layout engines, variable resolvers, constraint enforcers, instance resolvers, rendering pipelines, each evolved independently, each scheduling itself in ad hoc ways — produces two diseases:
- Back-dirties (concepts/back-dirty) — later systems invalidate earlier systems' work, forcing re-runs, potentially oscillating.
- Unclear ownership and blurred responsibilities — "systems fight over the same subtree" because none of them can assume its inputs are stable.
The runtime orchestration toward unidirectional flow pattern is the fix:
- Introduce a shared orchestration layer that owns when each subsystem runs.
- Run subsystems in a predictable order, topologically sorted by their inter-subsystem dependencies.
- Use the predictable order as a detection substrate for back-dirties that were previously noise.
- Eliminate back-dirties by refactoring subsystem responsibilities (push deps upstream, demote competing writers, separate compute-from-write phases).
- Converge toward unidirectional flow: source-of-truth changes → subsystem₁ → subsystem₂ → … → rendered output, never backward.
Canonical source: sources/2026-04-21-figma-rebuilt-foundations-of-component-instances:
"We improved how our various runtime systems work together by unifying them under a common framework. The new approach has better separation of concerns (systems no longer need branching logic that special-cases instances), and the data flow is now one-directional which improves predictability of side effects."
The two-step pattern¶
Step 1: unification. Subsystems stop scheduling themselves
independently. The orchestration layer has a schedule (usually per
edit / per tick / per frame) and calls each subsystem in order. Each
subsystem exposes compute() and apply() methods; the orchestrator
decides when.
Step 2: cleanup via visibility. Predictable execution order makes order-of-magnitude more back-dirties visible — if after ordering subsystem i before j, j's output still requires a second pass of i, there's an edge from j back to i that shouldn't exist. These were invisible under ad hoc scheduling (just noise in the perf trace); now they're discrete signals a team can enumerate and kill one by one.
Prerequisites¶
- Shared reactivity substrate. Figma picked push-based invalidation with automatic dependency tracking. A unified reactivity model is usually prerequisite — subsystems need to speak the same dirty / recompute language.
- Clear subsystem boundaries. If responsibilities are interleaved (instance resolution doing layout; layout doing variable evaluation), unification is blocked until the separation of concerns cleanup happens. Figma's post describes exactly this cleanup as part of the Materializer project.
- Willingness to break existing perf profiles. Porting each subsystem into the shared orchestration introduces "subtle differences in execution order and behavior." Figma absorbed this risk with side-by-side validation for months before rollout.
Why unidirectional flow is the correctness property¶
A fully unidirectional client runtime is a DAG: every subsystem S has a strict set of upstream subsystems it reads from and downstream subsystems it writes to, with no cycles. Given such a DAG, one pass through the topological order is guaranteed to produce a fixed point — no back-and-forth, no re-runs, no oscillation. The framework's scheduling becomes trivial (just walk the DAG); the only remaining work is per-node reactivity (which nodes in each subsystem to recompute, handled by push-based invalidation).
Figma frames this as "more closer to" unidirectional — an asymptote, not a claim of full achievement. The real-world practice is to kill back-dirties one at a time and monitor for new ones as the runtime grows.
Related patterns in other systems¶
- Redux / Flux — unidirectional data flow was the central design commitment. Same idea, applied to app state.
- Game engine ECS systems — per-frame subsystem scheduling in a strict order; classic unidirectional runtime.
- Spreadsheet recalc — cells form a DAG; one topological sweep per change.
- CI pipelines — stage graph is a DAG; no backward edges by construction.
- Kubernetes controller chains — level-triggered reconciles on idempotent writes; not fully unidirectional but structurally similar.
Figma's contribution is applying this discipline at the client runtime layer of a collaborative design editor, where historically subsystems had been written by independent teams without a shared orchestration contract.
Cost¶
- Migration effort. Years of subsystem evolution must be refactored into the new orchestration framework.
- Ongoing tax. New subsystems must fit the orchestration — no more "run whenever I notice I have work." Teams that want the velocity of the old model must be disciplined.
- Rollout risk. Order-of-execution changes produce subtle behavior differences in edge cases unit tests don't cover. The side-by-side runtime validation pattern is the de-risking mechanism.
Seen in¶
- sources/2026-04-21-figma-rebuilt-foundations-of-component-instances — canonical instance. Unifies layout / variable / instance / constraint runtimes; back-dirties named and partially eliminated; movement toward one-directional data flow as the stated architectural direction.
Related¶
- concepts/back-dirty — the anti-pattern this pattern eliminates.
- concepts/push-based-invalidation — the reactivity substrate subsystems share.
- systems/figma-materializer — one of the orchestrated subsystems; the project whose construction motivated the broader orchestration unification.
- patterns/unified-typespace-consolidation — sibling consolidation pattern on the data-model axis (Figma's parameter systems).
- patterns/side-by-side-runtime-validation — the de-risking discipline used to roll out a unified runtime.