5.9.1 Skyframe Data Model

recommended

Skyframe is the evaluator behind Bazel's incremental work. Its smallest useful model is a memoized computation: ask for a SkyKey, run the SkyFunction selected by that key's function name if no reusable result is available, record every requested dependency, and associate the resulting SkyValue with the key. This is an implementation model for interpreting Bazel, not a public API for BUILD or Starlark authors.1,2

This refines the first-response model in 2.4.1 Skyframe & Incrementality. Later infrastructure work can deliberately prune or preserve parts of this state; 6.6.9 Bottleneck-Driven Optimization covers those operational designs without changing the node model developed here.

How Skyframe turns one request into a reusable graph node
The key selects a computation family. Environment lookups discover dependencies before the result is stored under the original key.
SKYKEY
Identifies what to compute
function name + argument
(PACKAGE, //app)
SKYFUNCTION
The function name selects this computation family
The function requests dependencies through Environment
compute(key, env)
SKYVALUE
The completed result is stored for the original key
Future evaluations may reuse it
parsed package data
DEPENDENCIES DISCOVERED WHILE THE PACKAGE FUNCTION RUNS
(PACKAGE, //app) requests BUILD-file key
(PACKAGE, //app) requests directory-state key
A node is the evaluator entry keyed by SkyKey, not merely the returned SkyValue.

Read the three names as three different roles

A SkyKey identifies a computation. In current Bazel source it is effectively a pair of a SkyFunctionName and an argument. The function name selects the computation family. The argument distinguishes one request in that family. Keys are designed to be compact, stable map keys because a Bazel server can retain very many of them.1,2

A SkyFunction implements a computation family. Skyframe dispatches a key to the registered function for its function name and calls compute(key, env). The function may ask the environment for other keys. Those requests create the dependency edges that let Skyframe schedule work and later decide what may be stale. 5.9.2 Evaluation Model follows the request-and-restart protocol in detail.1,3

A SkyValue is the completed result returned for a key. The key is the identity of the request. The value is its data. A value is therefore not the graph node's identity, and it is misleading to use “node” to mean only the Java value object. In diagnostic discussions, a Skyframe node is best read as the evaluator's entry for a SkyKey, together with its result, dependency edges, error and evaluation state.1,2

That distinction prevents a common category error. Two keys in the same function family can produce two different values, while re-evaluating one key can produce a value equal to its previous value. The latter fact is what makes change pruning possible. 5.9.4 Incrementality Mechanism develops that mechanism.

Trace one request instead of memorizing class names

Suppose Bazel needs the parsed package //app. The names below are conceptual, not promises about bazel dump syntax or Java class names:

requested key:  (PACKAGE, //app)
function:       package-loading computation
dependencies:   keys for BUILD-file and directory state
result:         parsed package data

The evaluator first uses the key's function name to select the package-loading function. During computation, that function requests keys for the filesystem facts it needs. Their returned values become inputs to the package result, and the evaluator records edges from the package key to those dependency keys. This one trace contains the whole data model: key, dispatched function, dependency keys and values, then the value for the original key.1

Real diagnostic output may show families such as FILE_STATE, PACKAGE, CONFIGURED_TARGET, or ACTION_EXECUTION, with version-dependent spelling and arguments. Treat examples like FILECONTENTS:/tmp/foo from older documentation as illustrations of function family plus argument, not as a grammar to parse in production automation. Current SkyKey.getCanonicalName() also derives a string from functionName() and argument(), but both the internal types and dump representations remain implementation details.1,2

The graph is discovered during evaluation

An edge A → B means computing the value for key A requested the value for key B. It does not mean that A is a BUILD target depending on target B, nor that an action for A ran before an action for B. One retained Skyframe graph crosses filesystem, package, configured-target, artifact, action-execution, and other internal computations. This is why a raw dump or Skyscope view contains more node families than query, cquery, or aquery.1

For a successful evaluation, those dependency edges form a directed acyclic graph: a value cannot be completed through a dependency chain that leads back to itself. But “the graph is a DAG” does not mean Skyframe can assume that requested dependencies are always acyclic. Real evaluation includes cycle detection and reports cycles as errors. The DAG is the usable dependency structure of completed work. Cycle handling is part of making the evaluator robust.3

Also avoid picturing a fixed graph declared before the build. A function can discover dependencies while it computes—for example, resolving a symlink can reveal another filesystem key. Skyframe records the dependencies actually requested for that evaluation. The graph retained by the long-lived server is therefore internal state shaped by commands and invalidation, not a single canonical representation of the workspace.1

“Immutable” is a correctness contract, not an interface keyword

Skyframe documentation describes keys and values as immutable. The practical reason is straightforward: keys serve as hash-map identities, and cached values are compared and reused. Mutating either after publication would make lookup, dependency tracking, or equality-based reasoning unreliable.1

Do not over-read that statement as a guarantee enforced by the Java type system. SkyKey and SkyValue are interfaces. Their current declarations do not mechanically make every implementation immutable. Individual implementations must honor the contract, and some evaluator bookkeeping is necessarily mutable. In particular, the node entry around a key/value records evaluation and invalidation state even though the published key and completed value should be treated as immutable.2

The same precision matters for SkyFunction. “Deterministic and side-effect-free” is a useful ideal for a hermetic computation, but it is not a universal description of Bazel's implementations. Current source distinguishes hermetic, semi-hermetic, and non-hermetic function names. The official model also permits carefully managed outward effects such as writing outputs, while warning that unregistered reads can break incremental correctness. The safe consulting claim is: every input that can affect a result must be represented by a tracked dependency or an explicit non-hermetic invalidation mechanism.1,2

Use the model to sharpen an investigation

When an internal graph appears in a stack trace, dump, or visualization, ask four questions in order:

  1. What exact SkyKey identifies this node—both its function family and its argument?
  2. Which SkyFunction family computes it?
  3. Which dependency keys did that computation request, and what does each edge mean at this graph layer?
  4. What completed SkyValue, error, or incomplete evaluation state is retained for the key?

This vocabulary narrows hypotheses without pretending the internals are stable observability APIs. Use 5.6.8 Skyscope — Skyframe Visualizer or a Skyframe dump to find an internal neighborhood, then return to supported tools: query for declared targets, cquery for configured targets, aquery for actions, and profiles or execution logs for what actually ran.

key takeaway

A Skyframe computation is identified by a compact SkyKey, dispatched to the SkyFunction family named by that key, connected to every dependency key the function requests, and completed with a SkyValue. Read a graph node as the evaluator entry keyed by the SkyKey, not as the value object or a BUILD target.

The completed dependency structure is acyclic, but Skyframe still detects cycles during evaluation. Keys and completed values follow an immutability contract, while evaluator state remains mutable. Functions aim for tracked, deterministic inputs but are not universally side-effect-free. Internal key names and dump strings are diagnostic clues, not stable public APIs.

Check your understanding · 3 questions

1.Match each Skyframe concept to its role:

Drag each answer onto the matching prompt, or click an answer and then click a prompt

Answers
SkyKey
SkyFunction
SkyValue

2.What does a Skyframe dependency edge A → B mean?

Select one answer

3.Which cautions apply when interpreting the Skyframe data model?

Select all that apply

0 of 3 answered

Footnotes

  1. Skyframe — data model, dependency discovery, DAG semantics, effects, and mapping to Bazel computations 1 2 3 4 5 6 7 8 9 10

  2. Bazel upstream source — current SkyKey, SkyValue, SkyFunction, and SkyFunctionName interface contracts (verified 18 July 2026) 1 2 3 4 5 6

  3. Empyrean Evaluation — memoization model, cycle detection, and the production evaluator's additional responsibilities 1 2