Managing dbt models governance: sample questions

3 free practice questions on managing dbt models governance, one of the seven topics in the official v1.11 outline for the dbt Analytics Engineering Certification Exam — an estimated 10% of the exam, or about 6 of its 65 questions. Answers, reasoning and documentation links are all on this page.

Last updated . We revise these pages whenever dbt Labs revises the exam.

SHARE OF THE EXAM~10%our estimate
QUESTIONS HERE33 with runnable SQL
FORMATS SHOWN2of 6 on the exam

What this topic covers

Governance is a small topic with three subtopics — contracts that enforce a model's shape, constraints declared in YAML that push integrity down to the platform, and model versions with the deprecation path that goes with them — but it is heavily represented in the newest edition of the exam.

SUBTOPICS, FROM THE OFFICIAL OUTLINE · 3

  1. 01Adding contracts to models to ensure the shape of models
  2. 02Creating different versions of our models and deprecating the old ones
  3. 03Defining constraints in YAML to enforce data integrity at the platform level

Where the marks go

The reliable trap is treating the three as independent features. They are not: dbt only emits constraints when the model declares contract enforcement, because the contract is what forces every column to carry a declared name and type in the first place. The discrete-option question below turns on exactly that dependency.

Versioning has a second-order effect people miss. A versioned model's warehouse identifier comes from generate_alias_name, whose default appends the version — so shipping v2 changes the relation name that a BI tool is pointed at. The third question walks through which relations exist after the build.

3 sample questions, with answers

Nothing is hidden. Each question shows the correct answer, why it is right, why every other option is wrong, and the documentation page that settles it. Where the answer is a claim about SQL behaviour, there is a query you can run in the browser against a small sample schema.

Q1Multiple choiceMediumRunnable proof

fct_orders is a table model on Snowflake, and no constraints are declared on it. Consider its YAML. The model's SELECT list returns order_id, status, order_date, in that order, with matching data types. Which statement regarding dbt build --select fct_orders is true?

models:
- name: fct_orders
config:
materialized: table
contract:
enforced: true
columns:
- name: order_date
data_type: date
- name: order_id
data_type: number
- name: status
data_type: varchar
  • The build succeeds; dbt logs a contract warning that the column order no longer matches.
  • The build fails; the SELECT list must return columns in the order the YAML declares them.
  • The build succeeds; the contract check compares column names and data types, not position.CORRECT
  • The build succeeds; the created table's columns follow the model's SELECT order, and the YAML order is ignored.

WHY

An enforced contract makes dbt run a preflight check before the model is materialized: it gets the column names and data types the model's query will return, then compares that set against the columns declared in YAML. The comparison is done by column name — dbt fails the build only when a declared column is missing from the query, the query returns a column the contract does not declare, or a matched column's data_type differs, and it prints those rows in a mismatch table. Position in the SELECT list is not part of the compared shape, so reordering the same columns with the same types leaves the contract satisfied and the model builds. One related fact worth separating from the check itself, because it is easy to conflate: the ORDER of the columns in the table dbt creates does follow the contract. The docs say dbt "will include the column names, data types, and constraints in the DDL statements it submits to the data platform ... and order the columns per the contract instead of your dbt model." So the contract decides the physical column order of the built relation. The two facts sit either side of one line: order is not part of what the preflight check COMPARES, and it is entirely what the DDL FOLLOWS.

WHY THE OTHERS ARE WRONG

The build succeeds; dbt logs a contract warning that the column order no longer matches.
Contract enforcement is pass/fail at build time, not a warning channel — a violation raises an error and the model does not build. There is also no order-drift warning, because column order is never compared.
The build fails; the SELECT list must return columns in the order the YAML declares them.
Nothing in contract enforcement matches columns by ordinal position. dbt pairs each YAML column with the query's column of the same name, so a pure reorder produces no mismatch rows and no failure. (Ordering can matter when you hand-write DDL with constraints, but no constraints are declared here.)
The build succeeds; the created table's columns follow the model's SELECT order, and the YAML order is ignored.
This inverts the documented behaviour. The contract does not merely tolerate a different column order in your SELECT — it overrides it in the created table: dbt "will include the column names, data types, and constraints in the DDL statements it submits to the data platform ... and order the columns per the contract instead of your dbt model." So the YAML order is the one that survives into the relation, and the model's SELECT order is what gets ignored.
Q2Discrete option (DOMC)MediumRunnable proof

Each statement concerns constraints on a model whose contract is enforced. You will be shown the statements one at a time. For each statement, answer YES if it is true, NO if it is false.

In the exam these appear one at a time and you answer YES or NO to each, without seeing the rest. All of them, with their answers, are below.

  • A constraint may be declared at the model level as well as on a single columnYES
  • A constraint is declared under a column's data_tests keyNO
  • On Snowflake, a primary_key constraint is written into the model's DDL but not enforcedYES
  • On Postgres, a check constraint is enforced by the databaseYES
  • On BigQuery, a unique constraint is enforced by the platformNO
  • On Redshift, a check constraint is enforced by the platformNO

WHY

Declaration site: constraints take a constraints: list either under an individual column or under the model's own top-level constraints: key — the model-level form is what makes multi-column constraints expressible at all (a is true). Enforcement is a property of the data platform, not of dbt: dbt only templates the constraint into the DDL it emits, so what happens next depends on the warehouse. Snowflake accepts primary_key syntax and stores it as metadata, but the only constraint Snowflake actually enforces is NOT NULL — a duplicate key lands without complaint (c is true). Postgres is the outlier that enforces every constraint type dbt supports, check included, so a row failing the predicate aborts the statement (d is true).

WHY THE OTHERS ARE WRONG

A constraint is declared under a column's data_tests key
data_tests is the separate dbt-side mechanism: a query run after the build that fails the node when rows come back. Constraints live under their own constraints: key and become part of the DDL, so the platform — not dbt — does the checking.
On BigQuery, a unique constraint is enforced by the platform
BigQuery has no UNIQUE constraint at all, so dbt lists unique as unsupported there. Only not_null is enforced on BigQuery; primary_key and foreign_key are accepted as unenforced metadata.
On Redshift, a check constraint is enforced by the platform
Redshift does not support CHECK constraints. Its unique, primary_key, and foreign_key constraints are informational only, and not_null is the one type Redshift enforces.
Q3Multiple choiceHardRunnable proof

dim_customers is a versioned model: v2 is the latest_version, and v1 carried a deprecation_date that passed last quarter. Today you finish the retirement — you delete the v: 1 entry from the model's versions list and delete v1's SQL file. One downstream model still refs it with v=1. Which statement regarding the next dbt build is true?

  • The pinned ref resolves to the dim_customers_v1 relation left in the warehouse, and the run succeeds.
  • The pinned ref falls back to v2, and the run finishes with a deprecation warning.
  • The missing version raises a warning by default, and errors only under dbt build --warn-error.
  • The downstream model is skipped as an upstream failure, while the rest of the DAG builds.
  • The pinned ref has no target, so dbt errors during parsing and nothing builds.CORRECT

WHY

Refs are resolved at parse time, not at run time: at the start of every dbt invocation dbt reads all the files in the project and constructs a manifest containing every object, using the ref() calls it finds to infer dependencies and construct the DAG. The versions list is what declares which versions of dim_customers exist, so deleting the v: 1 entry removes the node that ref('dim_customers', v=1) was pointing at. dbt then raises a compilation error of the documented shape — a model depends on a node which was not found — and because that happens while the manifest is still being assembled, no SQL is compiled or executed and nothing builds. That is exactly what separates deletion from deprecation: a deprecation_date leaves the version in place and only emits warnings — DeprecatedModel to the producer, DeprecatedReference and UpcomingReferenceDeprecation to the producer and to any consumer who refs it — which is why the documented sunset path is to develop the new version, bump the latest, slate the old version for deprecation, update downstream references, and only then remove the old version.

WHY THE OTHERS ARE WRONG

The pinned ref resolves to the dim_customers_v1 relation left in the warehouse, and the run succeeds.
dbt does not drop relations for models you remove from the project, so dim_customers_v1 does survive in the warehouse — but ref resolves against the manifest, not the database. An orphaned relation with no node behind it never satisfies a ref.
The pinned ref falls back to v2, and the run finishes with a deprecation warning.
There is no fallback. An unpinned ref('dim_customers') resolves to latest_version, but v=1 pins the ref to one specific node; when that node no longer exists dbt errors instead of silently substituting another version. The deprecation warnings from last quarter came from the deprecation_date on the still-present v1 entry — once the entry is gone, so is the metadata that produced them.
The missing version raises a warning by default, and errors only under dbt build --warn-error.
--warn-error only promotes existing warnings to errors; it cannot demote an error to a warning. An unresolvable ref is a hard failure at any warn-error setting. Deprecation warnings are the thing --warn-error would escalate here, and those disappeared along with the v1 entry.
The downstream model is skipped as an upstream failure, while the rest of the DAG builds.
Skipping is a runtime behaviour: dbt skips a node whose parent errored during execution. This failure happens earlier, while the manifest is being assembled, so there is no executable DAG at all and nothing gets skipped.

Know whether managing dbt models governance is actually costing you marks.

The free readiness check is weighted like the real exam across all seven topics, so it tells you where you stand on this topic relative to the rest — which is the only version of that question worth answering before you book.

Take the free readiness check20 questions · ~15 minutes · no card

The other six topics

Keep reading

SOURCES

Every source above was read in full and last checked on .