2.2 Three Phases of a Build

Run bazel build //app:server and a line like this appears before the command finishes:

INFO: Analyzed target //app:server (14 packages loaded, 48 targets configured).

That summary reports loading and analysis work. Before ordinary tools produce outputs, Bazel reads declarations and turns reachable targets into an action plan. Execution then runs the needed actions that are not already satisfied by cache hits.

The target graph from 2.1 Directed Acyclic Graph (DAG) is therefore only the first layer. A target graph is not yet an action graph, and an action graph is not yet executed work. The phase model separates those layers so the log can tell you which responsibility produced a symptom.

Read The Boundary As Evidence

Start with 2.2.1 Loading, Analysis & Execution if loading and analysis still seem interchangeable. It assigns each phase a responsibility and gives you the mnemonic package -> target -> action -> artifact.

2.2.2 Reading Build Output applies that vocabulary to a failing command. In a simple phase-separated trace, a failure before INFO: Analyzed ... points toward packages, labels, BUILD or .bzl files, dependency declarations, or rule validation. A failure after it points toward execution, including compilers, linkers, tests, sandboxes, and tools producing outputs. Treat the line as a clue rather than a hard boundary because Skymeld may execute ready actions while analysis of the wider request continues.

The counters need the same phase context. packages loaded describes the requested package slice, not compilation. targets configured describes analysis work, not commands that ran. total actions is not a count of commands executed from scratch because cache hits, null builds, and already-valid outputs affect it.

The Plan Exists Before Tools Run

2.2.3 Static Action Graph explains why ordinary rules register actions, inputs, and outputs during analysis instead of compiling first and discovering the rest of the build later. That up-front plan makes bazel aquery, cache keys, parallel scheduling, and remote execution possible. Read it when a rule or language integration runs into Bazel's plan-first boundary.

2.2.4 Skymeld adds the scheduling caveat. Modern Bazel may overlap analysis and execution, especially when some top-level targets finish analysis before others. The responsibilities remain distinct even when their timing overlaps. This article is optional unless you are comparing timings or reading profiles.

The distinction also explains why bazel query can inspect the target graph without compiling anything and bazel aquery can show the planned actions without running them. Visibility or dependency-graph failures can stop the build on the declaration side, while sandbox failures concern an executing action's view of its inputs.

The eager-fetch-load-edge snippet makes a loading edge visible: its BUILD file loads an external helper, which buildfiles(deps(...)) reveals even though target deps() does not.

This phase boundary supports the action-focused models in 2.3 Hermeticity & Sandboxing, 2.4 Caching & Incrementality, and 2.5 Execution Strategies. Each asks a different question after Bazel has established what work the request means.

key takeaway

Loading discovers packages, analysis turns reachable targets into an action plan, and execution runs the needed actions. Even when Bazel overlaps that work, classifying a symptom by responsibility tells you which logs and debugging tools can explain it.