2.1 Directed Acyclic Graph (DAG)
When bazel build //app:server touches only a narrow slice of a large repository, it is tempting to call that caching. Sometimes cache contributes, but this section starts one layer earlier. Before Bazel can reuse anything, it has to know what the request means. //app:server is not a folder to sweep or a script to run from the top. It is a node in a directed graph, and the build begins by following the edges that lead to everything that node needs.
That graph explains why unrelated packages stay out of a small build, why a missing direct dependency can remain hidden until a refactor exposes it, why cycles are rejected instead of scheduled, and why bazel query 'deps(//app:server)' can teach you something useful without compiling a single file.
One Request, One Reachable Graph
The section begins with the basic vocabulary in 2.1.1 Nodes, Edges & Acyclicity. Labels name nodes, dependency attributes create directed edges, and the graph must stay acyclic so Bazel can derive a valid order. That article is the anchor: without nodes, edges, transitive closure, and acyclicity, the rest of the section becomes a bag of command tricks.
2.1.2 Laziness & Slicing then turns the same graph into a user-visible behavior. Bazel does not start from "everything in the repo." It starts from the requested target and works through the reachable slice. That is why a large repository can still answer a narrow build request with narrow work.
The middle of the section complicates the beginner picture deliberately. 2.1.3 Dependency Types explains that not every edge is one you typed: explicit BUILD-file dependencies, implicit rule or toolchain dependencies, and third-party module dependencies all feed the graph. 2.1.4 Common Dependency Issues shows what happens when the graph no longer matches reality: stale direct deps, accidental re-exports, and diamond-shaped external dependency pressure.
The last two articles make the graph inspectable. 2.1.5 Inspecting the Graph — Query Preview maps query, cquery, and aquery to the graph layers introduced here and continued in 2.2.1 Loading, Analysis & Execution. 2.1.6 Reverse Dependencies & Change Impact flips the direction with rdeps(), turning the DAG from "what do I need?" into "who might I affect?"
Dependencies Are Not Hints
The mental shift is that dependency declarations are not advisory metadata for humans. They are the coordinates Bazel uses to decide the shape of the request.
In many build tools, a dependency list can feel like a helper list near the real script. In Bazel, the list is part of the model. A deps edge changes reachability. A missing direct edge can hide behind a transitive one until a refactor removes the path. An extra edge widens the closure Bazel must consider. A toolchain edge may appear even though the BUILD file never named the compiler directly. The graph is both stricter and more useful than a casual "uses this somehow" note.
That is also why this section sits before the phase model and caching model. 2.2 Three Phases of a Build explains how Bazel turns reachable targets into actions. 2.4 Caching & Incrementality explains how prior work can be reused. But both depend on the earlier question: which part of the target graph is in scope for this command?
Stay With The Declared Graph First
BUILD attributes, target patterns, query expressions, dependency failures, and unexpectedly large closures all expose the same target graph. Start with three questions: which node did I request, which edges did Bazel follow, and which slice became reachable? Move to configured targets, the action graph, or incremental evaluation only when the declared graph cannot explain the observation.
Build The Graph Model Before The Tools
Read 2.1.1 Nodes, Edges & Acyclicity and 2.1.2 Laziness & Slicing as the foundation. Together they explain why Bazel can be selective before any cache discussion begins.
Then read 2.1.3 Dependency Types and 2.1.4 Common Dependency Issues as a pair for keeping dependencies accurate. They are most useful when real BUILD files start pulling in more than expected, or when code compiles only because an intermediate library happens to forward something today.
Finally, treat 2.1.5 Inspecting the Graph — Query Preview and 2.1.6 Reverse Dependencies & Change Impact as your first diagnostic toolkit. You do not need the full Level 5 query reference yet. You need enough to ask three everyday questions: what does this target need, why does this path exist, and who might break if I change this shared target?
When a graph question feels vague, turn it into one of these commands first:
bazel query 'deps(//app:server)'
bazel query 'somepath(//app:server, //lib:core)'
bazel query 'rdeps(//..., //lib:core)'
The exact labels will change, but the habit is reusable: inspect the forward closure, explain a path, then check reverse impact before refactoring.
A build request roots a reachable slice of the target DAG, and query tools let you inspect that structure before execution begins. Establish that declared graph before diagnosing phases, action identity, or incremental reuse.
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?