2.1 Directed Acyclic Graph (DAG)
bazel build //app:server names one target. Yet
bazel query 'deps(//app:server)' can reveal a much larger set. The useful
question is why those targets and files appear while most of the repository
does not. Each included node is reachable from //app:server along a
dependency path. Branches with no such path stay outside the request.
Once you can see that boundary, several Bazel behaviors line up. A narrow build
is a small reachable slice. A compiler target you never wrote in deps may
arrive through an implicit edge. A missing direct dependency can stay hidden
behind another path until a refactor removes it. A cycle leaves Bazel without a
valid dependency order.
Trace The Request Outward
2.1.1 Nodes, Edges & Acyclicity gives you the map: labels name nodes, dependency attributes create directed edges, and the graph must stay acyclic. 2.1.2 Laziness & Slicing shows Bazel using that map for one command, following only the paths reachable from the requested target. Read these two together: the first defines the structure, and the second explains why a large repository does not automatically mean a large build request.
Explain The Edge You Did Not Expect
Finding an unexpected node is only the start. The next question is who added
the edge that led there. 2.1.3 Dependency Types separates dependencies
written in BUILD files from those supplied by rules and toolchains, and asks
separately whether each dependency lives in the main repository or an external
one.
2.1.4 Common Dependency Issues covers the cases where the path itself needs attention: code relying on an undeclared direct dependency, an accidental re-export, or two branches that introduce incompatible external versions. Together, these articles turn “why is this here?” into a concrete investigation: classify the edge, trace its path, and decide whether the relationship is intentional.
Ask The Graph In Both Directions
2.1.5 Inspecting the Graph — Query Preview maps query, cquery, and aquery to the graph layers
they inspect. For the target graph, three questions cover most first
investigations: what does this target need, why does this dependency path
exist, and who depends on the target I am about to change?
2.1.6 Reverse Dependencies & Change Impact develops that last question into a change-impact
check by walking the graph backward.
Stay with the declared target graph while the question is structural. Move to configured targets when flags or platforms choose the edge, to the action graph when you need the concrete execution plan in 2.2.1 Loading, Analysis & Execution, and to 2.4.1 Skyframe & Incrementality when the question is about reuse across commands. This keeps an unexpected dependency, a configuration choice, and a cache miss from collapsing into one vague “Bazel did too much” problem.
Start from the requested target and trace reachability. If the resulting slice surprises you, classify the edge and explain its path. Before changing a shared target, walk the graph in reverse. Switch graph layers only when configuration, actions, or incremental reuse become part of the question.
Sections in this chapter · 6
The target graph: targets are nodes, dependencies are edges, cycles are forbidden.
How requested outputs select a reachable graph slice, and why laziness differs from incrementality.
Distinguishing who declares an edge from which repository owns its target.
Diagnosing diamond versions, stale or missing direct dependencies, and intentional re-export facades.
Choosing query, cquery, or aquery for the declared, configured, or action graph.
rdeps() answers the opposite of deps(): who depends on this target, and what could a change affect?