Data Lineage in dbt: Reading and Using the Dependency Graph
The question โwhat will break if I change this columnโ is one every data engineer eventually has to answer under time pressure, usually right before a deploy. In a project without lineage tracking, answering it means grepping through SQL files and hoping you found every reference. In dbt, the answer is a few clicks away, because every ref() and source() call has already been recorded as an edge in a graph dbt maintains automatically.
What the DAG Actually Represents
DAG stands for Directed Acyclic Graph โ directed because dependencies flow one way (upstream to downstream), acyclic because loops arenโt allowed (a model canโt depend on something that depends on it). Every node in the graph is either a source, a model, a seed, or a snapshot. Every edge is a ref() or source() call dbt found while parsing your project.
raw.orders (source) โ โผstg_orders (model) โ โผcustomer_orders (model) โโโ stg_customers (model) โ โผsales_summary (model)This graph isnโt something you draw manually or keep in sync by hand โ itโs derived entirely from the SQL you already wrote. Every time you add a ref() call, youโre editing the lineage graph, whether you think of it that way or not.
Viewing the Lineage Graph
The visual version of the DAG lives in dbtโs generated documentation site:
dbt docs generatedbt docs serveOpening the docs site and clicking the lineage icon on any model shows its full upstream and downstream chain, filterable by how many โhopsโ away you want to see. This is covered operationally in dbt Docs Generate and dbt Docs Serve โ this article focuses on how to actually use the graph once itโs in front of you, not how to produce it.
Using Lineage for Impact Analysis
The most practical use of the DAG isnโt admiring it โ itโs answering a specific question before you make a change: โif I alter this model, what else is affected?โ
# See everything downstream of a model before touching itdbt ls --select stg_orders+
# See everything upstream that a model depends ondbt ls --select +sales_summary
# See both directionsdbt ls --select +customer_orders+The + operator is graph syntax dbt borrows directly from its lineage structure โ a + after a model name means โand everything downstream,โ a + before means โand everything upstream.โ Running this before a schema change tells you exactly which models to re-test, without needing to read a single line of SQL.
Lineage and Selective Execution
Beyond analysis, the DAG is what makes selective builds possible โ running only the part of your project thatโs actually affected by a change, instead of rebuilding everything every time.
# Build a model and everything downstream of itdbt run --select stg_orders+
# Build a model and everything it depends on, but not further downstreamdbt build --select +sales_summaryOn a project with hundreds of models, this is the difference between a build that takes twenty minutes and one that takes two, because dbt only touches the subgraph thatโs actually relevant to what changed.
Reading a Lineage Graph for Debugging
When a number in a dashboard looks wrong, the lineage graph is the fastest path to the root cause โ trace backward from the broken model through its upstream dependencies until you find the layer where the data first looks wrong.
sales_summary โ numbers look wrong here โ โฒ (trace upstream)customer_orders โ check this next โ โฒstg_orders โ and this โ โฒraw.orders (source) โ if this is stale, check Source FreshnessThis backward-tracing approach turns โsomething is wrong somewhere in the pipelineโ into a structured, layer-by-layer investigation instead of a guessing exercise.
Lineage Includes More Than Just Models
A common misconception is that the DAG only shows SQL models. In practice, it includes every node type dbt tracks: sources, seeds, snapshots, and even exposures (documented downstream consumers like BI dashboards, covered in a later guide on dbt Exposures). This means the lineage graph can genuinely answer โwhich dashboards depend on this table,โ not just โwhich models do.โ
Why the Graph Stays Accurate Automatically
Unlike a manually maintained architecture diagram, the DAG canโt drift out of date, because it isnโt a separate artifact someone updates โ itโs regenerated directly from your projectโs actual SQL every time dbt parses it. If a modelโs dependencies change, the lineage graph reflects that the next time you run dbt docs generate or any dbt command that parses the project, with zero manual maintenance.
Common Mistakes
Treating the DAG as documentation you write separately. Thereโs nothing to write โ the graph exists because of ref()/source() calls, not because of a diagram someone drew. Adding descriptions and column docs in YAML enriches the graph; it doesnโt create it.
Ignoring upstream lineage when debugging. Itโs tempting to stare at the broken model itself, but the actual problem is very often several layers upstream. Trace backward before assuming the bug is local.
Not using selection syntax for large projects. Running full dbt run on every change in a 300-model project wastes warehouse compute and developer time. The + graph operators exist specifically to avoid this.
Summary
| Concept | What It Gives You |
|---|---|
| DAG | Automatically derived dependency graph from ref()/source() calls |
dbt ls --select model+ | Lists everything downstream of a model |
dbt ls --select +model | Lists everything upstream of a model |
| Lineage graph (docs site) | Visual, clickable version of the same dependency data |
Lineage in dbt isnโt a feature you opt into โ itโs a byproduct of writing normal dbt models correctly. The payoff is that impact analysis, selective builds, and root-cause debugging all become graph traversal problems instead of manual detective work.