Data Engineering  /  dbt

๐Ÿ”„ dbt โ€” Data Build Tool 60 guides ยท updated 2026

Analytics engineering with SQL โ€” models, tests, sources, and Jinja macros that turn raw warehouse tables into trustworthy, documented data products.

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:

Terminal window
dbt docs generate
dbt docs serve

Opening 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?โ€

Terminal window
# See everything downstream of a model before touching it
dbt ls --select stg_orders+
# See everything upstream that a model depends on
dbt ls --select +sales_summary
# See both directions
dbt 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.

Terminal window
# Build a model and everything downstream of it
dbt run --select stg_orders+
# Build a model and everything it depends on, but not further downstream
dbt build --select +sales_summary

On 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 Freshness

This 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

ConceptWhat It Gives You
DAGAutomatically derived dependency graph from ref()/source() calls
dbt ls --select model+Lists everything downstream of a model
dbt ls --select +modelLists 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.