Essential dbt Packages for Production Projects
Master the must-have dbt packages for every production project: dbt-utils, dbt-expectations, dbt-audit-helper, dbt-codegen, elementary, dbt-project-evaluator, and more β all configured for an Amazon Redshift environment.
Essential dbt Packages for Production Projects
The dbt package ecosystem means you should never write a surrogate key function, a pivot macro, or a date spine from scratch. The community has already solved these problems, tested them at scale, and published them on dbt Hub. This module covers the packages that belong in every production dbt-Redshift project, why they exist, and how to use their most important features.
Package Management Fundamentals
All packages are declared in packages.yml and installed with dbt deps.
# packages.yml
packages:
# Core utilities β install this in every project
- package: dbt-labs/dbt_utils
version: 1.3.0
# Great Expectations-style data quality tests
- package: calogica/dbt_expectations
version: 0.10.4
# Dataset comparison for migrations and refactors
- package: dbt-labs/audit_helper
version: 0.10.0
# Code generation from sources and warehouse introspection
- package: dbt-labs/dbt_codegen
version: 0.12.1
# Data observability and anomaly detection
- package: elementary-data/elementary
version: 0.16.1
# DAG quality and documentation coverage enforcement
- package: dbt-labs/dbt_project_evaluator
version: 0.13.0
# dbt artifact mart (run metadata in warehouse tables)
- package: brooklyn-data/dbt_artifacts
version: 2.3.0# Install all declared packages
dbt deps
# Update a specific package
dbt deps --upgradeAlways pin package versions exactly in packages.yml. Floating versions (>=1.0.0) cause non-reproducible builds when packages release breaking changes. Update versions deliberately, after reading the changelog.
1. dbt-utils: The Foundation Package
dbt-labs/dbt_utils is the most important dbt package β it provides core macros and tests that nearly every other package depends on.
Surrogate Keys
-- models/marts/dim_customers.sql
select
-- Generate a surrogate key from multiple natural keys
{{ dbt_utils.generate_surrogate_key(['user_id', 'account_id']) }} as customer_key,
user_id,
account_id,
email,
customer_segment
from {{ ref('stg_customers') }}generate_surrogate_key produces a consistent MD5 hash across warehouses. On Redshift it compiles to md5(cast(coalesce(cast(user_id as varchar), '_dbt_utils_surrogate_key_null_') || '-' || ...) as varchar).
Star β Select All Except
-- Select all columns except internal load metadata columns
select
{{ dbt_utils.star(
from=ref('stg_orders'),
except=['_fivetran_synced', '_fivetran_deleted', 'loaded_at']
) }}
from {{ ref('stg_orders') }}Union Relations
-- models/marts/fct_all_events.sql
{{ config(materialized='incremental', incremental_strategy='append') }}
{{
dbt_utils.union_relations(
relations=[
ref('stg_web_events'),
ref('stg_mobile_events'),
ref('stg_api_events')
],
column_override={"event_timestamp": "timestamp"},
include=["event_id", "user_id", "event_type", "event_timestamp", "properties"]
)
}}Date Spine
-- models/marts/dim_date.sql
{{ config(materialized='table', dist='all', sort='date_day') }}
{{
dbt_utils.date_spine(
datepart="day",
start_date="cast('2020-01-01' as date)",
end_date="cast('2030-12-31' as date)"
)
}}Useful Generic Tests
models:
- name: fct_orders
columns:
- name: order_id
data_tests:
# Ensure no values are empty strings (not caught by not_null)
- dbt_utils.not_empty_string
- name: total_amount
data_tests:
# Value must be between two other columns in the same row
- dbt_utils.expression_is_true:
expression: ">= 0"
data_tests:
# Table-level: ensure at least N rows exist
- dbt_utils.recency:
datepart: hour
field: order_date
interval: 6 # fails if newest order is > 6 hours old
# Row count is within expected range
- dbt_utils.equal_rowcount:
compare_model: ref('stg_orders')
# Not-null proportion (useful for raw source tables)
- dbt_utils.not_null_proportion:
at_least: 0.95
column_name: customer_id2. dbt-expectations: Great Expectations for dbt
calogica/dbt_expectations adds 50+ data quality tests inspired by the Great Expectations library β far beyond dbt's built-in generic tests.
# packages.yml already includes calogica/dbt_expectations
models:
- name: fct_orders
data_tests:
# Table must have exactly these columns (catches accidental schema changes)
- dbt_expectations.expect_table_columns_to_match_ordered_list:
column_list:
- order_id
- customer_id
- order_date
- status
- total_amount
- customer_segment
- region
# Row count must be within 20% of yesterday's count
- dbt_expectations.expect_table_row_count_to_be_between:
min_value: 1000
max_value: 10000000
columns:
- name: order_date
data_tests:
# Date must be between two bounds
- dbt_expectations.expect_column_values_to_be_between:
min_value: "'2020-01-01'"
max_value: "current_date"
row_condition: "order_date is not null"
# No gaps in daily date coverage (critical for time-series facts)
- dbt_expectations.expect_row_values_to_have_data_for_every_n_datepart:
date_col: order_date
date_part: day
test_start_date: "'2024-01-01'"
- name: total_amount
data_tests:
# Value distribution: mean should be between $50 and $500
- dbt_expectations.expect_column_mean_to_be_between:
min_value: 50
max_value: 500
# Standard deviation check (detects data distribution drift)
- dbt_expectations.expect_column_stdev_to_be_between:
min_value: 10
max_value: 1000
# No outliers > 3 sigma from the mean
- dbt_expectations.expect_column_values_to_be_within_n_stdevs:
n: 3
- name: email
data_tests:
# Regex validation for email format
- dbt_expectations.expect_column_values_to_match_regex:
regex: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
row_condition: "email is not null"3. dbt-audit-helper: Safe Refactoring and Migrations
dbt-labs/audit_helper is your safety net when refactoring models or migrating from one SQL pattern to another. It compares two relations row-by-row and column-by-column.
Compare Row Counts
# As a dbt operation
dbt run-operation audit_helper.compare_row_counts --args '{
"a_relation": "analytics.marts.fct_orders",
"b_relation": "analytics.marts_v2.fct_orders"
}'Compare Column Values
# Column-by-column value comparison between old and new model
dbt run-operation audit_helper.compare_column_values --args '{
"a_relation": "analytics.marts.fct_orders",
"b_relation": "analytics.marts_v2.fct_orders",
"primary_key": "order_id",
"columns_to_compare": ["total_amount", "status", "customer_segment"]
}'Output:
column_name | match_count | a_only_count | b_only_count | mismatch_count
----------------|-------------|--------------|--------------|---------------
total_amount | 99,847 | 0 | 0 | 153
status | 100,000 | 0 | 0 | 0
customer_segment| 99,993 | 0 | 0 | 7
Workflow: Safe Migration
# Step 1: Build new model alongside old one
dbt run --select fct_orders_v2
# Step 2: Compare β zero mismatches required before cutover
dbt run-operation audit_helper.compare_column_values --args '{...}'
# Step 3: Compare row counts
dbt run-operation audit_helper.compare_row_counts --args '{...}'
# Step 4: If clean, rename and switch refs4. dbt-codegen: Eliminate Boilerplate
dbt-labs/dbt_codegen generates YAML and SQL from your warehouse schema β eliminating the tedious manual work of writing source configurations and base models.
Generate Source YAML from Redshift
# Generate sources.yml for an entire schema
dbt run-operation codegen.generate_source --args '{
"schema_name": "raw",
"database_name": "analytics",
"generate_columns": true,
"include_descriptions": true,
"table_pattern": "orders%" # only tables matching this pattern
}'Output (paste into models/staging/sources.yml):
version: 2
sources:
- name: raw
database: analytics
schema: raw
tables:
- name: orders
description: ""
columns:
- name: order_id
description: ""
- name: customer_id
description: ""
# ... all columnsGenerate Base Models
# Generate a staging model SQL from a source table
dbt run-operation codegen.generate_base_model --args '{
"source_name": "raw",
"table_name": "orders",
"leading_commas": false
}'Output (paste into models/staging/stg_orders.sql):
with source as (
select * from {{ source('raw', 'orders') }}
),
renamed as (
select
order_id,
customer_id,
status,
total_amount,
created_at,
updated_at
from source
)
select * from renamedGenerate Model YAML
# Generate schema.yml for an already-materialized model
dbt run-operation codegen.generate_model_yaml --args '{
"model_names": ["fct_orders", "dim_customers"],
"upstream_descriptions": true
}'5. Elementary: Data Observability
elementary-data/elementary adds anomaly detection as native dbt tests β it monitors your data for volume, freshness, schema, and value distribution anomalies without external tooling.
# models/marts/facts/schema.yml
models:
- name: fct_orders
data_tests:
# Alert if row count drops/spikes more than 20% vs. trailing 7-day average
- elementary.volume_anomalies:
timestamp_column: order_date
sensitivity: 3 # standard deviations
days_back: 14 # training window
# Alert if newest row is older than expected
- elementary.freshness_anomalies:
timestamp_column: order_date
sensitivity: 3
columns:
- name: total_amount
data_tests:
# Alert if value distribution changes significantly
- elementary.column_anomalies:
column_anomalies:
- null_count
- null_rate
- average
- standard_deviation
- max_value
timestamp_column: order_date
sensitivity: 3
- name: status
data_tests:
# Alert if category distribution changes (e.g., sudden spike in Cancelled)
- elementary.dimension_anomalies:
timestamp_column: order_date
sensitivity: 3Elementary writes test results to a mart of tables in your warehouse. Configure the schema:
# dbt_project.yml
vars:
elementary:
elementary_database: analytics
elementary_schema: elementary # dedicated schema for observability tables
days_back: 30 # how many days of history to retain# Install elementary and run its models
dbt deps
dbt run --select elementary
# Generate the Elementary report (HTML observability dashboard)
pip install elementary-data
edr report6. dbt-project-evaluator: DAG Quality Enforcement
dbt-labs/dbt_project_evaluator analyzes your dbt project's structure and flags anti-patterns.
# Run all evaluator checks
dbt build --select package:dbt_project_evaluatorKey checks it runs:
| Evaluator Model | What it catches |
|---|---|
fct_undocumented_models | Models missing description |
fct_missing_primary_key_tests | Models without unique + not_null tests |
fct_model_fanout | Models with too many direct parents (spaghetti DAG) |
fct_rejoining_of_upstream_concepts | Models that join two branches sharing a common ancestor |
fct_direct_join_to_source | Mart models that join directly to raw sources |
fct_hard_coded_references | Models using hardcoded schema names instead of ref() |
fct_unused_sources | Sources declared but never referenced |
Configure thresholds in your dbt_project.yml:
# dbt_project.yml
vars:
dbt_project_evaluator:
documentation_coverage_target: 80 # % of models that must be documented
test_coverage_target: 80 # % of models that must have tests
max_fanout_models: 5 # max direct parents per model
primary_key_test_macros: # what counts as a PK test
- dbt_utils.unique_combination_of_columns
- unique7. dbt-artifacts: Run Metadata in Your Warehouse
brooklyn-data/dbt_artifacts loads dbt run metadata (model timings, test results, source freshness) into warehouse tables β enabling dashboards over your pipeline health.
# packages.yml
packages:
- package: brooklyn-data/dbt_artifacts
version: 2.3.0# dbt_project.yml
vars:
dbt_artifacts:
dbt_artifacts_database: analytics
dbt_artifacts_schema: dbt_artifactsUse on-run-end hooks to capture metadata after every run:
# dbt_project.yml
on-run-end:
- "{{ dbt_artifacts.upload_results(results) }}"Query pipeline health directly from Redshift:
-- Which models are slowest in production?
select
model_execution_time_ms,
name,
schema_name,
materialization,
run_started_at
from analytics.dbt_artifacts.fct_dbt__model_executions
where run_started_at > current_date - 7
order by model_execution_time_ms desc
limit 20;
-- Test pass rate over time
select
date_trunc('day', run_started_at)::date as run_date,
count_if(status = 'pass') as passed,
count_if(status = 'fail') as failed,
count(*) as total,
round(count_if(status = 'pass') * 100.0 / count(*), 1) as pass_rate_pct
from analytics.dbt_artifacts.fct_dbt__test_executions
where run_started_at > current_date - 30
group by 1
order by 1;Recommended packages.yml Template
# packages.yml β production-ready template for dbt + Redshift
packages:
# Core β required
- package: dbt-labs/dbt_utils
version: 1.3.0
# Testing
- package: calogica/dbt_expectations
version: 0.10.4
# Refactoring and migration safety
- package: dbt-labs/audit_helper
version: 0.10.0
# Code generation
- package: dbt-labs/dbt_codegen
version: 0.12.1
# Observability and anomaly detection
- package: elementary-data/elementary
version: 0.16.1
# DAG quality enforcement
- package: dbt-labs/dbt_project_evaluator
version: 0.13.0
# Run metadata in warehouse
- package: brooklyn-data/dbt_artifacts
version: 2.3.05 Practice Questions
Why should package versions always be pinned exactly in packages.yml rather than using a range like `>=1.0.0`?
What does `dbt_utils.star(from=ref('stg_orders'), except=['loaded_at'])` do?
When would you use dbt-audit-helper's `compare_column_values` operation?
What does `elementary.volume_anomalies` detect that a standard `dbt_utils.recency` test cannot?
The `fct_direct_join_to_source` evaluator check flags which anti-pattern?
What does the `on-run-end` hook with `dbt_artifacts.upload_results(results)` enable?
Key Takeaways
- Always pin package versions exactly in
packages.ymlβ floating versions cause silent, non-reproducible breakages. dbt-utilsis mandatory: surrogate keys,star(),date_spine,union_relations, and advanced generic tests belong in every project.dbt-expectationsadds 50+ statistical and structural data tests that go far beyond dbt's built-in suite.dbt-audit-helperis your safety net during refactoring β compare column values and row counts between old and new models before cutting over.dbt-codegeneliminates boilerplate: generatesources.yml, base staging models, and schema YAML from your warehouse catalog in seconds.elementaryadds statistical anomaly detection as native dbt tests β volume spikes, distribution shifts, and freshness anomalies are caught without external observability tools.dbt-project-evaluatorenforces DAG quality standards and documentation coverage in CI, preventing architectural debt from accumulating.dbt-artifactsloads run metadata into warehouse tables, enabling SQL-based pipeline health dashboards directly in Redshift.