FlashGenius Logo FlashGenius
dbt Certification ยท Domain 2 of 6

dbt Analytics Engineering Certification โ€” Model Governance

Contracts, model versioning, deprecation, and access controls โ€” the guardrails that make dbt projects production-safe.

~10%Exam Weight
3Core Topics
dbt 1.5+Feature Set
High ValueLess Studied

What This Domain Covers

Domain 2 covers the governance layer of dbt โ€” features that enforce structure, manage change, and control who can use what. These were introduced in dbt 1.5+ and appear consistently on the exam.

๐Ÿ“œ

Contracts

Enforce model shape โ€” column names, data types, and constraints

๐Ÿ”ข

Model Versions

Safely evolve models without breaking downstream consumers

๐Ÿ”

Access Controls

Public, private, and protected access levels for cross-project use

Why Governance Matters on the Exam

Governance features are newer (dbt 1.5+) and less commonly practiced โ€” which means candidates who study them carefully have an edge. The exam tests your ability to:

  • Choose the right access level (public/protected/private) for a given scenario
  • Understand when and how to apply a model contract
  • Version a model and deprecate old versions correctly
  • Know which access level allows cross-project references

๐ŸŽฏ Key Exam Insight

Only public models can be referenced across dbt projects. Private models can only be used within the same group. Protected models (the default) can be used anywhere in the same project. This distinction is a frequent exam topic.

Key Concepts Deep Dive

Click each topic to expand study notes.

๐Ÿ“œ
Model Contracts
Enforcing the shape of a model
High Frequency โ–พ
  • A contract guarantees that a model always produces a specific set of columns with specified data types
  • Defined in the model's .yml file under contract: {enforced: true} with explicit column definitions
  • dbt validates the contract at run time โ€” if the model produces different columns/types, the run fails
  • Contracts require materialized: table or incremental โ€” not views or ephemeral
  • Constraints (NOT NULL, PRIMARY KEY, etc.) can be added to contract columns; support varies by platform
  • When a contract is active and an incremental model runs, the data warehouse enforces constraints on new rows automatically (e.g. NOT NULL constraint prevents null inserts)
๐Ÿ’ก Exam scenario: "You want to test that incremental inserts never violate NOT NULL without scanning the whole table." Answer: use a contract with a NOT NULL constraint โ€” the warehouse enforces it at insert time.
๐Ÿ”ข
Model Versioning & Deprecation
Evolve models without breaking consumers
High Frequency โ–พ
  • Model versions let you maintain multiple versions of a model simultaneously (e.g., v1 and v2)
  • Defined in the model's .yml with a versions: block listing version numbers and optional defined-in paths
  • Consumers reference specific versions with {{ ref('model_name', version=2) }}
  • latest_version in the .yml specifies which version ref('model_name') (without version) resolves to
  • deprecation_date marks a version as deprecated โ€” dbt emits a warning when it's referenced
  • Deprecation allows a migration window: consumers can upgrade to v2 before v1 is removed
๐Ÿ’ก Use versioning when making breaking changes (removing/renaming columns). Instead of breaking consumers, create a v2, set it as latest, and give consumers time to migrate off v1.
๐Ÿ”
Model Access Levels
public ยท protected ยท private
High Frequency โ–พ
๐ŸŒ

Public

Accessible from any dbt project. Use for stable, shared interfaces (e.g. mart models consumed by other teams).

๐Ÿ›ก๏ธ

Protected

Default access. Accessible anywhere within the same project. Safe for most models.

๐Ÿ”’

Private

Only accessible within the same group. Use for internal implementation details you don't want other teams to depend on.

  • Access is set with access: public | protected | private in the model's .yml
  • Groups โ€” models can be organized into groups; private models are scoped to their group
  • Cross-project references require the upstream project to declare models as public
๐Ÿ’ก Memory: Public = anyone, Protected = same project (default), Private = same group only.

Domain 2 Study Checklist

Check off each item as you study. Domain 2 is focused โ€” master all of these.

Progress: 0 / 10 complete
โœ“
Concept

Understand what a model contract enforces

Column names, data types, and optional constraints (NOT NULL, PRIMARY KEY)

โœ“
Concept

Know which materializations support contracts

Table and incremental only โ€” not view or ephemeral

โœ“
Practice

Write a contract for a model with 3+ columns and data types

Add contract: {enforced: true} and explicit column definitions in .yml

โœ“
Concept

Understand the three access levels: public, protected, private

Know what each allows and when to use each

โœ“
Exam Prep

Know that only public models can be referenced cross-project

Critical distinction โ€” this appears on the exam

โœ“
Concept

Understand what groups do for private models

Private models are scoped to their group โ€” other groups in the same project cannot ref() them

โœ“
Practice

Create a versioned model with v1 and v2

Set latest_version, add a deprecation_date to v1, and ref() v2 from a downstream model

โœ“
Exam Prep

Know the ref() syntax for versioned models

{{ ref('model_name', version=2) }} โ€” version is a keyword argument

โœ“
Concept

Understand deprecation_date behavior

dbt emits a warning when a deprecated version is referenced โ€” it doesn't fail immediately

โœ“
Exam Prep

Read the dbt Model Governance docs page

docs.getdbt.com/docs/collaborate/govern โ€” review all three sections

Quick Reference

Key YAML syntax for Domain 2 โ€” these patterns appear in exam questions.

Model Contract

models: - name: fct_orders config: contract: enforced: true columns: - name: order_id data_type: integer constraints: - type: not_null - name: customer_id data_type: integer

Model Versioning

models: - name: dim_customers latest_version: 2 versions: - v: 1 deprecation_date: 2026-09-01 - v: 2 # Referencing a specific version: {{ ref('dim_customers', version=2) }}

Access & Groups

# Define a group groups: - name: marketing owner: email: team@company.com # Apply access to a model models: - name: fct_campaigns config: group: marketing access: public

Access Level Summary

# public โ€” cross-project accessible access: public # protected โ€” same project (default) access: protected # private โ€” same group only access: private # Default if not set: access: protected

Domain 2 Practice Quiz

5 exam-style questions on governance.

Domain 2 Study Plan

Domain 2 is focused โ€” 3โ€“4 days of targeted study is sufficient.

Day 1 ยท Contracts

  • Read the dbt Contracts documentation thoroughly
  • Add a contract to an existing model in your practice project
  • Intentionally break the contract (change a column name) and observe the error

Day 2 ยท Model Versioning

  • Take a staging model and create v1 and v2 versions
  • Set latest_version to v2 and add a deprecation_date to v1
  • Verify that ref('model') resolves to v2 and ref('model', version=1) resolves to v1

Days 3โ€“4 ยท Access, Groups & Exam Prep

  • Define a group in your project and assign models to it
  • Set one model to private โ€” verify that a model outside the group cannot ref() it
  • Do 10โ€“15 practice questions specifically on Domain 2 topics
  • Review the Model Governance docs page end-to-end

Common Exam Mistakes โ€” Domain 2

1

Applying a contract to a view model

Contracts only work with table/incremental

โ–พ
What Goes Wrong

Setting contract: enforced: true on a view or ephemeral model causes a dbt compilation error. Contracts require the model to be materialized as a table or incremental.

The Fix

Before adding a contract, confirm the model is materialized as table or incremental. If it's a view, change the materialization or remove the contract.

๐Ÿ›ก๏ธ Habit: Contract = table/incremental only. Always check materialization before enabling contract enforcement.
2

Assuming protected models are accessible cross-project

Cross-project requires public, not just protected

โ–พ
What Goes Wrong

Attempting to ref() a protected model from another dbt project fails at compile time. Protected = same project only.

The Fix

Explicitly set access: public on any model you want other dbt projects to be able to reference.

๐Ÿ›ก๏ธ Remember: public is the ONLY access level that crosses project boundaries.
3

Confusing deprecation_date with deletion

Deprecated models still run โ€” they just warn

โ–พ
What Goes Wrong

Candidates think deprecation_date automatically removes or stops the model from running after the date. It only emits a warning โ€” it doesn't fail or stop anything.

The Fix

Deprecation is a communication mechanism โ€” it warns consumers to migrate. You must manually delete the deprecated version after the migration window closes.

๐Ÿ›ก๏ธ deprecation_date = warning, not deletion. Teams need to manually remove deprecated versions after consumers migrate.
4

Missing column definitions when a contract is enforced

Partial column specs cause runtime failures

โ–พ
What Goes Wrong

A contract with enforced: true but incomplete column definitions (missing data_type) will fail. dbt requires all contracted columns to have an explicit data type.

The Fix

Every column listed under a contract must include a data_type. Review your platform's supported data types in the dbt docs to ensure correct values.

๐Ÿ›ก๏ธ Every contracted column needs: name + data_type at minimum.
5

Using ref() without version= for a versioned model

Unexpected version resolution

โ–พ
What Goes Wrong

ref('dim_customers') without a version= argument resolves to the latest_version, not necessarily v1. If latest_version is set to v2, your model may silently use the new schema.

The Fix

During migration windows, explicitly pin consumers to a version: ref('dim_customers', version=1) until you're ready to upgrade to v2.

๐Ÿ›ก๏ธ When multiple versions exist, always be explicit about which version you intend to use.

Frequently Asked Questions

What is the default access level for a dbt model? +
Protected is the default. Models without an explicit access: config behave as protected โ€” accessible anywhere within the same dbt project, but not from other projects.
Can a model have both a contract and model versions? +
Yes โ€” in fact, contracts and versions work well together. When you version a model, you can define separate contracts for v1 and v2 with different column shapes. This is the recommended pattern when making breaking schema changes.
What happens when you run dbt and a contracted model's output doesn't match the contract? +
The dbt run fails with a contract violation error before the model is materialized. dbt validates the compiled query's output columns against the contract definition and raises an error if there's a mismatch in column names or data types.
How does a private model differ from simply not documenting a model? +
A private model is enforced by dbt โ€” if a model outside the group tries to ref() a private model, dbt will raise a compile-time error. Lack of documentation has no enforcement; other models can still reference undocumented models freely.
What does latest_version control in model versioning? +
latest_version determines which version ref('model_name') (without an explicit version argument) resolves to. Setting it to v2 means all unversioned references automatically point to v2. Downstream models that haven't explicitly pinned to v1 will use v2.
Are constraints in contracts always enforced by the data warehouse? +
It depends on the data platform. Snowflake, BigQuery, and Databricks have varying support for constraint enforcement. Some platforms support constraint definitions but only as metadata (not enforced at write time). Check platform-specific dbt documentation for which constraints are enforced vs informational.
Official Resources

Domain 2 Study Resources

Official dbt Labs governance documentation