Lineage and State
This domain tests your ability to use dbt's lineage graph, project artifacts, and state-comparison features to manage, debug, and refactor dbt projects efficiently. It accounts for 13% of the exam, making it one of the mid-weight domains. Expect questions on reading the DAG, understanding what artifacts contain, how --select interacts with state, how model versions and contracts are declared, and how to perform impact analysis before making breaking changes.
DAG Lineage and the Docs Site Lineage Graph
Core Concepts
The DAG (Directed Acyclic Graph) is dbt's internal representation of the dependency relationships between all nodes in a project. Nodes include models, sources, seeds, snapshots, tests, exposures, and metrics. Edges in the DAG are directed from upstream to downstream, and the graph is acyclic — no circular references are permitted.
dbt builds the DAG by parsing ref() and source() function calls in model SQL files. Every ref('model_name') creates a directed edge from the referenced model to the current model. If a model references a source using source('schema', 'table'), that source node is included as an upstream dependency.
The Docs Site
Running dbt docs generate produces the documentation site artifacts. Running dbt docs serve launches a local web server to browse them. The lineage graph in the docs site is an interactive visualization of the DAG. Key behaviors:
- You can focus on any node and view its full upstream and downstream lineage.
- The graph respects the same
--selectand--excludesyntax used indbt run. - Exposures appear as downstream nodes, representing BI tools or other consumers of dbt models. They enable end-to-end lineage from sources through models to dashboards.
- The
dbt docs generatecommand must be re-run to reflect project changes; the site does not auto-update.
Node Selection Syntax Refresher
Understanding the DAG is inseparable from node selection:
| Syntax | Meaning |
|---|---|
model_name | Exactly that node |
+model_name | model_name and all upstream nodes |
model_name+ | model_name and all downstream nodes |
+model_name+ | model_name plus all upstream and downstream |
@model_name | model_name, all upstream, and all downstream parents of those upstream |
1+model_name | One level of upstream parents only |
model_name+2 | Two levels of downstream children only |
Common traps:
- Confusing
+model_name(upstream) withmodel_name+(downstream). The+is a graph operator and its position relative to the name determines direction. - Thinking
@model_nameis equivalent to+model_name+. The@also includes the tests and parents of upstream models, making it more expansive. - Assuming
dbt docs generateincludes test results — it generates documentation and the manifest, but run results are separate (see Artifacts section). - Forgetting that sources and exposures are valid node types in the lineage graph and can be selected with
source:andexposure:prefixes.
Worked Scenario
Requirement: A data engineer wants to see every model that depends on fct_orders, to assess the blast radius before refactoring it.
Reasoning: Use dbt ls --select fct_orders+ to list all downstream nodes. In the docs site lineage graph, click fct_orders and use the "Focus" feature to see only connected nodes, then expand downstream. This is impact analysis — a core use case of the lineage graph.
Artifacts: manifest.json and run_results.json
Overview of dbt Artifacts
dbt generates artifacts — JSON files — during various commands. These files are the foundation for state comparison, CI/CD optimization, and observability.
| Artifact | Generated by | Key contents |
|---|---|---|
manifest.json | dbt compile, dbt run, dbt test, dbt docs generate, most commands | Full project graph: nodes, sources, exposures, macros, metadata, unique_ids, compiled SQL |
run_results.json | dbt run, dbt test, dbt build, dbt seed, dbt snapshot | Execution results: status (success/error/skip), timing, adapter response per node |
catalog.json | dbt docs generate (with --catalog flag behavior) | Column-level metadata fetched from the warehouse: data types, row counts |
sources.json | dbt source freshness | Freshness check results per source |
semantic_manifest.json | dbt Semantic Layer commands | MetricFlow semantic graph |
manifest.json Deep Dive
The manifest is the most important artifact. It is a complete, machine-readable snapshot of the project at compile time.
Key fields to know:
nodes: All model, test, seed, and snapshot nodes keyed by unique_id (e.g.,model.project_name.model_name)sources: Source nodes keyed by unique_id (e.g.,source.project_name.schema.table)exposures: Exposure nodesparent_map/child_map: Explicit DAG edgesmetadata: dbt version, adapter type, generated_at timestampcompiled_sql/raw_sql: Per-node SQL
The unique_id format is critical: <node_type>.<project_name>.<node_identifier>. For models: model.jaffle_shop.orders. For tests: test.jaffle_shop.not_null_orders_id.abc123.
run_results.json Deep Dive
The run_results file captures what happened during execution.
Key fields:
results: Array of result objects, each with:unique_id: Matches the manifeststatus:success,error,warn,skip,pass,failexecution_time: Seconds elapsedadapter_response: Rows affected, bytes processedmessage: Error message if applicablefailures: Count of test failures
Common traps:
- Thinking
manifest.jsoncontains execution results — it does not. The manifest contains the project definition, not run outcomes. - Confusing
catalog.jsonwithmanifest.json. The catalog has warehouse metadata (column types frominformation_schema); the manifest has project definition (SQL, configs, dependencies). - Assuming
run_results.jsonis generated bydbt compile— it is not. Compile only generates the manifest. - The
statusfield in run_results uses different values for models vs. tests: models usesuccess/error/skip; tests usepass/fail/warn/error/skip.
Bold caption: Artifact generation matrix — which command produces which file
Worked Scenario
Requirement: A CI pipeline needs to check which models failed in the last run to send alerts. Which artifact and which field should the pipeline read?
Reasoning: Read run_results.json, iterate over the results array, and filter for objects where status == "error". The unique_id field identifies the failing node, and message contains the error text. The manifest is not needed for this task.
Unlock with a paid plan
7 more sections (about 2,440 words) continue below. Every paid plan unlocks all dbt Analytics Engineering Certification study guides, the full question bank and mock exams, from $4.97/month.