2.1.2 Laziness & Slicing

P.2.4 Five Properties named laziness and slicing before explaining the machinery behind them. That machinery is the target graph from 2.1.1 Nodes, Edges & Acyclicity: Bazel starts from the target you asked for, follows its transitive dependency closure, and keeps the rest of the repository outside the request. A build is not "the whole repo". It is the reachable part of the DAG for this command.1,2,3

What does Bazel inspect for bazel build //foo:foo?
It follows dependency paths from //foo:foo. The targets it can reach form the dependency closure. Unrelated branches stay outside this command.
Reachable targets: the dependency closure

Packages on this path load BUILD files for //foo and //common only.

request
//foo:foo

Top-level target for this command

//foo:lib

Direct dependency

//foo:helpers

Direct dependency

//common:base

Transitive dependency beneath //foo:lib

Unreachable branch
//bar:orphan

No path from the request into this subgraph. Not loaded or analyzed for this build.

Laziness is reachability: only targets in the closure are analyzed, and later only actions needed for requested outputs run. Same workspace, different request, different slice.

Reachability carves out the slice

bazel build //foo:foo means "work backward from //foo:foo and everything it needs," not "inspect every target in the workspace." If a branch has no path into that closure, it is outside the build. That is the core idea behind Bazel's laziness: reachability gives Bazel a precise boundary for the current request, so it can do the minimal work needed for the outputs you asked for.1,3,4

Slicing starts at package loading

Because 0.1.3 Package is the unit Bazel loads from disk, slicing shows up before any compiler or test runner starts. During loading, Bazel loads the necessary BUILD files for the initial targets and their transitive closure of dependencies. During analysis, it decides what work to do while still reading only BUILD files at that stage. The phase names come in 2.2.1 Loading, Analysis & Execution. The important point here is simpler: unreachable packages never enter the request in the first place.1,2

Laziness is different from incrementality

Laziness answers "what is in scope for this command?" Incrementality answers "what changed since the last command?" Skyframe starts from the top-level build request, then each SkyFunction asks for the nodes it needs until the dependency graph involved in that build has been discovered. 2.4.1 Skyframe & Incrementality is the next layer of the story: it explains how Bazel remembers and recomputes prior work, while laziness explains why unreachable branches never become part of the current request at all.2,5

key takeaway

Laziness is reachability applied to a build request. Slicing is the visible result: Bazel loads only the packages whose BUILD files sit on the requested target's dependency closure, analyzes only reachable targets, and later executes only the actions needed for requested outputs. Big repository, small request, small slice.1,2,7

extra

Requested outputs are sliced too

The same reachability rule shows up one layer later in the action graph. When a target has several outputs, Bazel only builds the requested files and the files they directly or indirectly depend on, so only the reachable actions execute. That is why BUILD declarations are recipes, not a script of mandatory steps: Bazel can work backward from requested outputs instead of eagerly running every action a target could possibly produce. 2.2.1 Loading, Analysis & Execution goes deeper into that action-level view.7,8,9

Check your understanding · 3 questions

1.What does Bazel's laziness guarantee when you run bazel build //foo:foo?

Select one answer

2.True or false: laziness vs incrementality in Bazel.

Choose True or False for each sentence

Laziness and incrementality answer the same question from different angles.
Laziness asks what is in scope for this command. Incrementality asks what changed since the last command.

3.Which command shows the set of packages Bazel must load to understand the dependency closure of //foo:foo?

Select one answer

0 of 3 answered

Footnotes

  1. Intro to Bazel — Bazel loads the BUILD files relevant to the target 1 2 3 4

  2. Build programs with Bazel — loading phase loads the necessary BUILD files for the initial targets and their transitive dependency closure, and loading/analysis read only BUILD files to determine work 1 2 3 4

  3. Dependencies — Bazel inspects the entire transitive closure of a target's dependencies during a build 1 2

  4. Bazel Training 101 (Part 3): Mental model — Bazel as a lazy transformation that constructs the minimal work needed for the outputs you asked for

  5. Skyframe — evaluation starts from the top-level build request and each SkyFunction requests the nodes it needs to finish that request

  6. Query guidedeps() for reachable closure and buildfiles(deps(...)) --output package for the package set behind that closure

  7. Rules — Bazel only builds requested files and the reachable transitive dependencies of those requested outputs 1 2

  8. Sponsored Session: Writing Bazel Rules - Instructor: Jay Conrod — action laziness as "subset of the graph, subset of actions, then only uncached actions run"

  9. Building Monorepo gRPC Services with Bazel — BUILD files as declarative recipes and Bazel working backward from the requested output