5.9 Skyframe
A source file changes, Bazel prints INFO: Build completed successfully, and
almost nothing runs. On another day, an apparently harmless edit causes a long
loading phase or a broad rebuild. “Bazel noticed the file” is too coarse an
explanation for either result. Between the filesystem event and an executed
tool lies Skyframe: Bazel's retained graph of computations and the dependencies
they discovered.
The useful consultant model is not “memorize Bazel's internal classes.” It is trace a claim through layers of evidence. Identify the changed input, find the computation that represents it, follow the dependency relationships that can carry the change, and stop as soon as a retained result proves equal. Only then ask whether an action reached cache lookup or tool execution. This section builds that model without turning internal Java APIs into advice for BUILD or Starlark authors.
Carry the Level 2 model forward
2.4.1 Skyframe & Incrementality introduced the deliberately simple model: the long-lived
server retains an in-memory graph, invalidates the affected reverse dependency
closure after an input change, and uses change pruning when recomputation
produces the same value. Here that model gains precise names. A SkyKey
identifies one computation, its SkyFunction requests dependencies and computes
a SkyValue, and the retained edges connect those computations. Invalidation
marks candidate nodes dirty along reverse edges. Checking can prove a node clean,
and recomputation that yields an equal SkyValue stops the change from
propagating farther.
That mapping adds diagnostic precision without replacing the Level 2 cache model. Skyframe graph reuse comes first in the cache evidence ladder. Local output/disk reuse and remote AC/CAS reuse answer later action-execution questions, while BEP, profiles, and execution logs supply evidence about what the invocation actually reached.
Build the investigation in three passes
Name what the evaluator is doing
Start with 5.9.1 Skyframe Data Model. It separates the identity of a
computation (SkyKey), the function family that computes it (SkyFunction),
the completed result (SkyValue), and the dependency edges retained around
them. That vocabulary keeps a Skyframe node distinct from a BUILD target, an
action, or merely the Java value object.
Then read 5.9.2 Evaluation Model as the control-flow companion. A computation requests dependencies through its environment. Missing values make it yield and restart later rather than block an evaluator thread. The same requests record the edges that make later invalidation possible. Together, these two articles explain what an internal graph edge means and how it comes into existence.
Locate the expensive or surprising neighborhood
Once the basic protocol is clear, 5.9.3 Key SkyValue Types provides the map needed for real investigations. Filesystem state, directory listings, packages, configured targets, artifacts, and action execution are interacting graph families—not one fixed pipeline. Use the changed input and observed edges to choose a neighborhood instead of inferring a universal chain from type names.
Explain what propagated—and what did not
5.9.4 Incrementality Mechanism is the payoff for rebuild diagnosis. It separates a node being marked dirty, being verified clean without recomputation, and being recomputed to an equal value. Those outcomes explain how a filesystem change can enter the graph yet stop before a downstream action is reconsidered. They also prevent “dirty,” “cache miss,” and “tool executed” from collapsing into the same vague idea of a rebuild.
Finally, 5.9.5 Filesystem Watching covers how a long-lived Bazel server can
narrow the set of local paths checked between commands. Read it when startup
filesystem checking, platform behavior, or --watchfs is relevant. A watcher
supplies candidate changes. It does not replace Skyframe's dependency tracking
or prove that an action ran.
The final extra branch, 5.9.6 Node Restart Cost, zooms in on repeated work inside an internal computation. It explains how serial dependency discovery can replay progressively longer prefixes, and why grouping independent requests or checkpointing sequential discovery can help internal Bazel code. This is specialist implementation guidance, so skip it unless a profile or code investigation points specifically at SkyFunction restart cost.
Replace the pipeline story with an evidence ladder
The tempting picture is a straight line from “file changed” to “target rebuilt.” That skips several different questions:
- What external fact changed, and how did Bazel discover it?
- Which concrete Skyframe key represents that fact?
- Which retained computations depend on it, directly or indirectly?
- Which dirty nodes were verified clean, recomputed, or changed in value?
- Did an action become eligible for cache lookup, receive a hit, or execute?
Each answer constrains the next. A changed source file need not invalidate its package declaration. A dirty action-related node need not execute a tool. A tool can execute and still produce an equal result that prevents downstream work. The section's central mental shift is therefore from narrating a broad “rebuild” to identifying the first graph-layer outcome that actually differs.
This also sets a boundary around internal evidence. Skyframe dumps and
5.6.8 Skyscope — Skyframe Visualizer can reveal a retained neighborhood, but internal key names,
class shapes, and edges are version-sensitive. Correlate them with supported
views: query for declared targets, cquery for configured targets, aquery
for planned actions, and profiles or execution logs for execution evidence.
Trace: Someone reports that “Bazel rebuilt everything.” Which sequence of facts would turn that report into a Skyframe explanation?
Reveal
Start with the changed external input and the concrete Skyframe key or graph family that represents it. Follow retained dependency edges, distinguishing dirty nodes from nodes verified clean or recomputed to an equal value, until you find the first value that changed. Only then identify the first action proven to have reached cache lookup or execution. Any missing link is the next evidence to collect.
Choose the shortest useful reading path
For general consulting work, read 5.9.1 Skyframe Data Model, 5.9.2 Evaluation Model, 5.9.3 Key SkyValue Types, and 5.9.4 Incrementality Mechanism in that order. That path gives you the node model, the edge-creation protocol, the graph-family map, and the rules that stop change propagation.
Return to the extra 5.9.5 Filesystem Watching article when the question concerns between-command filesystem scanning or watcher fallback. Continue to the final extra 5.9.6 Node Restart Cost only when working on Bazel's Java internals or when profiling points to replay inside a SkyFunction. Neither is required before you can trace an ordinary incremental-build symptom.
Avoid beginning with a large graph dump. Without a specific changed input and a precise question, its many node families encourage pattern matching rather than diagnosis. Begin at the supported graph or execution view closest to the symptom, then use Skyframe internals to explain the smallest relevant neighborhood.
Skyframe internals are most useful as an evidence model for incrementality: identify the changed input, map it to a concrete computation, follow recorded dependencies, distinguish dirty checking from value change, and only then ask whether an action hit a cache or executed. Learn the vocabulary and evaluation protocol first. Use node-family details, restart costs, and filesystem watching only when the investigation reaches those layers.
Sections in this chapter · 6
How SkyKey identity, SkyFunction dispatch, SkyValue results, and tracked dependency edges form a Skyframe evaluator node.
The Environment lookup, missing-value restart, and tracked-input contract of SkyFunction evaluation.
Filesystem, package/configured-target, and artifact/action SkyValue families and the concrete dependency edges needed to trace rebuilds without assuming a universal chain.
How dirty-node checking, value-version equality, and change pruning limit Skyframe recomputation independently of action-cache hits.
How --watchfs narrows Bazel's between-command filesystem diff, including platform scope and conservative fallback.
Why O(N) serial SkyFunction restarts can cause O(N²) replay work, and how internal Bazel code limits it.