Generic tests (unique, not_null) check data after it lands. Contracts reject
bad builds before they land.
models:
- name: mart_daily_revenue
config:
contract:
enforced: true
columns:
- name: day
data_type: date
constraints: [not_null]
- name: revenue_cents
data_type: numeric
constraints: [not_null]
Rules
- Contract the marts, not every staging model. Enforce at the boundary consumers read (marts); keep staging flexible for messy sources.
- Constraints are the executable half of the contract:
not_null,primary_key,foreign_key,check. dbt renders them as real DDL on supported warehouses. - A contract break fails
dbt build. That is the point — a renamed column or widened type stops the pipeline instead of silently shifting a dashboard. - Warehouse support varies (BigQuery/Snowflake/Databricks enforce; Postgres adapter renders constraints). Know your engine before promising enforcement.