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_v2

Version 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 migration

Pin 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: date

Migration 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

Leave a Comment

Your email address will not be published. Required fields are marked *