dbt Analytics Engineering Certification · 13% of the exam

Lineage and state: free practice questions

5 sample questions from our 33-question bank for this domain — answers and explanations included. These are the same scenario-based style as the real dbt Labs exam.

1. A data platform team wants to publish a stable, versioned public interface for `fct_orders` so that downstream consumers in other dbt projects are protected from accidental breaking changes. They set `access: public` and `contract: {enforced: true}` on the model, and define explicit column names and data types in the YAML. A junior engineer then opens a PR that renames a column in `fct_orders.sql` from `order_total` to `total_order_amount` but does NOT update the YAML contract. What will happen when dbt compiles this model, and why?

  • A. dbt will raise a contract violation error at compile time because the compiled model's output columns no longer match the columns declared in the enforced contract, preventing the run from proceeding.✓ Correct
  • B. dbt will issue a warning but still compile and run the model successfully, because contract enforcement only blocks deployment in production environments.
  • C. dbt will silently succeed at compile time but raise a schema test failure at run time when the `not_null` or `accepted_values` tests detect the missing column.
  • D. dbt will raise an error only if the model is referenced by a cross-project `ref()`, because contract enforcement is only triggered for public models consumed outside the current project.
Explanation

When `contract: {enforced: true}` is set, dbt validates at **compile time** that the model's SQL produces columns that exactly match the names and data types declared in the YAML. Renaming a column without updating the contract causes an immediate compile-time error — the run never executes. Option B is incorrect: contract enforcement is not environment-scoped; it applies in any environment where dbt compiles the model. Option C is incorrect: contract violations are not surfaced through schema tests — they are a separate compile-time check independent of tests. Option D is incorrect: contract enforcement is triggered whenever the model is compiled, regardless of whether it is consumed by a cross-project `ref()`; the `access: public` setting controls discoverability and cross-project referencing, but contract enforcement is separate and always active when `enforced: true`.

2. A developer is preparing to refactor `stg_payments.sql`, which feeds into three intermediate models and ultimately into `fct_orders`. Before making any changes, they run `dbt ls --select stg_payments+` and see a list of 12 downstream models. They then run `dbt ls --select +stg_payments` and see only 2 upstream nodes. What do these two commands collectively tell the developer, and which operator should they use if they want to select BOTH all ancestors AND all descendants of `stg_payments` in a single command?

  • A. The `+` prefix selects upstream; the `+` suffix selects downstream. Use `+stg_payments+` to select both ancestors and descendants in one command.✓ Correct
  • B. The `+` prefix selects downstream; the `+` suffix selects upstream. Use `stg_payments*` to select both directions at once.
  • C. Both `+stg_payments` and `stg_payments+` select all related nodes regardless of direction; they are interchangeable. Use either form.
  • D. The `+` prefix selects upstream; the `+` suffix selects downstream. Use `stg_payments*` to glob all related models in one command.
Explanation

In dbt's node selection syntax, a leading `+` (e.g., `+stg_payments`) selects the node plus all of its ancestors (upstream dependencies), while a trailing `+` (e.g., `stg_payments+`) selects the node plus all of its descendants (downstream dependents). Combining both as `+stg_payments+` selects the full lineage in both directions in a single command. Option B incorrectly reverses the meaning of the prefix and suffix operators, and `stg_payments*` is a glob that matches node names starting with 'stg_payments', not a lineage selector. Option C is wrong because the two operators are not interchangeable—they have distinct directional meanings. Option D correctly identifies the prefix/suffix meanings but incorrectly proposes `stg_payments*` as the bidirectional operator; the correct syntax is `+stg_payments+`.

3. An analytics engineer inspects `manifest.json` and `run_results.json` together to implement a custom data observability script. They want to find, for each model: (1) which nodes it depends on, and (2) whether it succeeded in the last run. Which fields from each artifact should they join on? (Select TWO — one from each artifact) (Select TWO)

  • A. `manifest.json` → `nodes.<unique_id>.depends_on.nodes` for upstream dependency lists✓ Correct
  • B. `manifest.json` → `nodes.<unique_id>.compiled_sql` to identify which models ran
  • C. `run_results.json` → `results[].unique_id` to match results back to manifest nodes✓ Correct
  • D. `run_results.json` → `results[].execution_time` as the join key between artifacts
  • E. `manifest.json` → `nodes.<unique_id>.path` as the primary identifier for joining
Explanation

The correct approach is to use **`manifest.json` → `nodes.<unique_id>.depends_on.nodes`** to get the list of upstream dependencies for each model, and **`run_results.json` → `results[].unique_id`** as the matching key to look up each node's execution status. `unique_id` is the stable identifier shared across both artifacts, making it the correct join key. `compiled_sql` is query text, not an identifier. `execution_time` is a metric, not a join key. `path` is a file path that is not guaranteed to be unique across packages and is not the standard join key.

4. A data engineer needs to refactor a widely used model called `fct_orders`. Before making any changes, they want to understand which downstream models will be affected. Which dbt command or selector BEST supports this impact analysis?

  • A. `dbt run --select fct_orders`
  • B. `dbt ls --select fct_orders+`✓ Correct
  • C. `dbt ls --select +fct_orders`
  • D. `dbt test --select fct_orders`
Explanation

`fct_orders+` (trailing `+`) selects `fct_orders` and all of its downstream dependents, giving a full picture of what would be affected by changes to the model. `+fct_orders` (leading `+`) selects all upstream parents of `fct_orders`, which answers "what does this model depend on" — not the impact downstream. `dbt run --select fct_orders` only runs that single model and doesn't reveal downstream impact. `dbt test --select fct_orders` runs tests on that model alone and also provides no downstream impact information.

5. An analytics engineer notices that after adding a new column to `dim_customer.sql` (with no other changes), the CI job using `--select state:modified+` picks up not only `dim_customer` but also three downstream models. Why are the downstream models included?

  • A. Because `state:modified` also detects changes in downstream models that reference `dim_customer`, even if their SQL is unchanged
  • B. Because the `+` suffix on `state:modified+` selects all downstream dependents of any modified node✓ Correct
  • C. Because `run_results.json` from the prior run marked those downstream models as `warn`, triggering reselection
  • D. Because dbt recompiles all models whenever a ref() dependency changes, causing all compiled SQL hashes to change
Explanation

The **`+` suffix** on `state:modified+` instructs dbt to include all downstream descendants of every modified node. `dim_customer` is detected as modified (its compiled SQL changed), and then the `+` traverses the DAG forward to include all models that depend on it. `state:modified` without `+` would select only `dim_customer` itself. `run_results.json` statuses do not influence state-based selection. dbt does not recompile or re-hash downstream models simply because an upstream model changed — their own SQL hashes remain the same.

28 more questions in this domain

Practice the full bank with instant grading, flashcards, and a timed mock exam.

Start practicing free