dbt Versioned Models
Model versioning lets you run multiple versions of the same model simultaneously in your warehouse. Version 1 of fct_orders continues serving existing consumers while version 2, with breaking changes, is available to consumers who are ready to migrate. This prevents breaking changes from disrupting teams that depend on your models.
The Problem Without Versioning
Scenario: Team A builds fct_orders and many dashboards depend on it. Team B wants to refactor fct_orders: rename columns, change types, drop columns. Without versioning: Team B changes fct_orders directly. All dashboards break immediately. Teams scramble to update their queries. With versioning: Team B creates fct_orders v2 alongside existing fct_orders v1. Existing dashboards continue using v1. New dashboards are built on v2. Teams migrate from v1 to v2 at their own pace. Team B deprecates v1 once all consumers migrate.
Defining a Versioned Model
Versioned models are declared in YAML. Create the SQL files with a version suffix in the filename.
File Structure
models/marts/ fct_orders_v1.sql ← version 1 SQL fct_orders_v2.sql ← version 2 SQL schema.yml ← declares both versions
schema.yml Declaration
version: 2
models:
- name: fct_orders
latest_version: 2 ← which version is "current"
versions:
- v: 1
defined_in: fct_orders_v1 ← points to fct_orders_v1.sql
deprecation_date: 2025-12-31 ← when v1 will be removed
- v: 2
defined_in: fct_orders_v2Version SQL Files
fct_orders_v1.sql (original)
select
order_id,
cust_id, ← old column name
order_dt, ← old column name
total_usd
from {{ ref('stg_orders') }}fct_orders_v2.sql (refactored)
select
order_id,
customer_id, ← renamed from cust_id
order_date, ← renamed from order_dt
amount_dollars, ← renamed from total_usd
status ← new column added
from {{ ref('stg_orders') }}What Gets Created in the Warehouse
dbt run creates both versions: analytics.fct_orders_v1 ← serves existing consumers analytics.fct_orders ← alias for the latest version (v2)
Referencing Versioned Models
Reference the latest version (recommended for new consumers)
from {{ ref('fct_orders') }}
-- resolves to fct_orders (latest = v2)Pin to a specific version
from {{ ref('fct_orders', v=1) }}
-- resolves to fct_orders_v1
-- use this to avoid breaking existing models during migrationPin to the latest version explicitly
from {{ ref('fct_orders', v=2) }}Deprecation Warnings
When a version has a deprecation_date set and the date is approaching, dbt emits a warning during runs:
$ dbt run WARNING: Model fct_orders.v1 is deprecated. deprecation_date: 2025-12-31 Consumers still using this version: 3 models Please migrate to fct_orders.v2 before the deprecation date.
Contracts with Versioned Models
Combine versioning with contracts for maximum stability. The contract defines the guaranteed output shape for each version:
models:
- name: fct_orders
latest_version: 2
versions:
- v: 1
config:
contract:
enforced: true
columns:
- name: order_id
data_type: integer
- name: cust_id
data_type: integer
- v: 2
config:
contract:
enforced: true
columns:
- name: order_id
data_type: integer
- name: customer_id
data_type: integer
- name: order_date
data_type: dateMigration Workflow
Step 1: Create v2 SQL file and declare version in schema.yml Step 2: Run dbt — both v1 and v2 exist in warehouse Step 3: Share v2 with consumers and ask them to migrate Step 4: Monitor which models still reference v1 using: dbt ls --select fct_orders.v1+ Step 5: Set deprecation_date on v1 to alert remaining consumers Step 6: Once all consumers migrate, delete fct_orders_v1.sql and remove v1 from schema.yml
Listing Models by Version
# List all versions of fct_orders dbt ls --select fct_orders # Run only v2 dbt run --select fct_orders.v2 # Run all consumers of v1 (to find migration targets) dbt ls --select fct_orders.v1+
When to Use Versioning
- A mart model has multiple consumers across different teams
- You need to make breaking changes (rename/remove columns, change types)
- Your project participates in dbt Mesh with cross-project references
- You want to give consumers a migration window before removing old logic
