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.

The dbt Lineage Graph: A Practical Guide to Reading It Correctly

Itโ€™s easy to open the lineage graph in dbtโ€™s documentation site, admire how connected everything looks, and move on without actually using it for anything. The graph is genuinely useful, but only once you know how to filter it, read direction correctly, and extract a specific answer from it rather than just browsing it as a pretty diagram.


What Youโ€™re Actually Looking At

The lineage graph, accessible through dbt Docs Serve, is a visual rendering of the same DAG covered conceptually in Data Lineage (DAG) โ€” nodes are sources, models, seeds, and snapshots; edges are ref() and source() calls. Direction matters: an arrow points from an upstream dependency toward the downstream model that consumes it.

raw.orders โ”€โ”€โ–ถ stg_orders โ”€โ”€โ–ถ customer_orders โ”€โ”€โ–ถ sales_summary

Reading this left to right: raw.orders feeds stg_orders, which feeds customer_orders, which feeds sales_summary. If youโ€™re standing at customer_orders and looking left, thatโ€™s โ€œupstreamโ€ โ€” what this model depends on. Looking right is โ€œdownstreamโ€ โ€” what depends on this model.


Filtering to a Manageable View

On any project beyond a handful of models, the full graph is visually overwhelming โ€” dozens or hundreds of nodes with no way to focus on what matters for the question you actually have. Every lineage view supports filtering to a specific model plus a configurable number of hops in either direction.


Using the Graph for a Real Impact-Analysis Question

Say you need to change a columnโ€™s meaning in stg_orders โ€” perhaps status needs a new value added. The graph answers, without reading a single line of SQL: which models filter or aggregate on status, and transitively, which dashboards or reports ultimately depend on those models. Click stg_orders, expand full downstream, and every affected model is visible in one view โ€” the alternative is manually grepping for status across every SQL file and hoping you didnโ€™t miss a reference.


The Graph Includes Exposures, Not Just Models

A detail thatโ€™s easy to miss: if your project documents exposures โ€” BI dashboards, reports, or other downstream consumers formally declared in YAML (covered in a later guide on dbt Exposures) โ€” those appear in the lineage graph too, as terminal nodes. This means the graph can genuinely answer โ€œwhich dashboards break if I change this,โ€ not just โ€œwhich modelsโ€ โ€” provided exposures have actually been declared for the dashboards that matter.


Command-Line Equivalent for Scripting

The visual graph is best for exploration; for scripting or CI checks, the same graph traversal is available via dbt ls with graph operators, as covered in dbt run:

Terminal window
# Everything downstream of stg_orders, machine-readable
dbt ls --select stg_orders+ --output json

This is what a CI job would use to determine, programmatically, which tests need to run for a given pull requestโ€™s changed files โ€” the visual graph and the command-line selector are two views onto exactly the same underlying dependency data.


Common Misreadings of the Graph

Confusing upstream and downstream direction. Itโ€™s a common mistake under time pressure to trace the wrong direction and conclude a change is safe when it isnโ€™t (or vice versa) โ€” always confirm which direction youโ€™re looking before drawing a conclusion.

Assuming the graph shows column-level lineage by default. Most setups show model-level lineage (which models depend on which) rather than column-level (which specific output column derives from which specific input column) โ€” column-level lineage requires additional tooling or a newer dbt/adapter feature set, and isnโ€™t universally available.

Treating an unconnected node as an error. A model with no visible upstream in the graph might genuinely have no dependencies (a seed-only model, or one generating synthetic data) โ€” not every node needs incoming edges.


Comparing Lineage Before and After a Refactor

When restructuring a section of a project โ€” splitting one large model into several smaller staging models, for instance โ€” comparing the lineage graph before and after the change is a quick sanity check that the refactor preserved the intended dependency structure. A model that should now depend on three new staging models, but the graph shows it still pointing at the old single model, indicates the ref() calls werenโ€™t fully updated โ€” a mistake thatโ€™s easy to make manually and immediately obvious once you look at the actual rendered graph rather than trusting that the refactor was complete.

Common Mistakes

Only using the graph for browsing, never for a specific question. The graphโ€™s real value is answering โ€œwhat breaks if I change thisโ€ before you change it, not admiring the overall shape of the project.

Not declaring exposures for dashboards that matter. Without them, the lineage graphโ€™s downstream view stops at the last dbt model โ€” it canโ€™t tell you which actual dashboards are affected, even though thatโ€™s usually the question that matters most to a business stakeholder.

Relying on the graph without corroborating it against actual test coverage. The graph shows dependency structure, not correctness โ€” a model can be perfectly connected in lineage and still be untested and wrong.

Summary

Use caseHow the lineage graph helps
โ€What does this model depend on?โ€Filter to upstream view
โ€What breaks if I change this?โ€Filter to downstream view, including exposures
Onboarding to an unfamiliar projectTrace backward from a known dashboard/table
CI impact analysisdbt ls --select model+ --output json, the scriptable equivalent

The lineage graph earns its keep the moment you use it to answer a specific, concrete question โ€” reserved for actual impact analysis and debugging, itโ€™s one of the most practical tools dbt gives you for working confidently in an unfamiliar or rapidly-changing project.