dbt for Transformation
Transformations as code your whole team can review
dbt turns transformation into versioned SQL models with dependency resolution, tests, docs, and re-runnable builds. ref() replaces copy-pasted joins and makes the warehouse a codebase.
What you will learn
- Structure a dbt project: sources, staging, marts
- Use ref() for dependency-driven builds
- Add tests and docs so models are provable and discoverable
What is this?
dbt is a command-line tool (and the holistic concept) that treats SQL transformations as a project: models are SELECT statements in files, ref() declares dependencies, and dbt builds them in order, testing and documenting as it goes. It turned ELT's 'T' into reviewable, versionable software.
Why do you need it?
- Dependency garbage (copy-pasted CTEs) is the #1 modeling rework; ref() kills it.
- Model + test + doc in one repo is the modern governance story.
- It is the lingua franca concept behind nearly every analytics-engineering job posting.
Mental model
A cookbook where each recipe references other recipes by name
dbt ref() + models
You write 'use the tomato sauce recipe' instead of pasting the sauce inline. When the sauce changes, every dish updates; the kitchen (dbt build) bakes dishes in dependency order and the taste tests (tests) run automatically.
How it works
- Sources: definitions of raw tables (FROM {{ source('shop','orders') }}).
- Models: SELECTs with ref('stg_orders') resolved to real dependencies by dbt.
- Layers: staging (clean+type), intermediates (business joins), marts (facts/dims), materialized as views/tables/incremental.
- Tests: uniqueness, not_null, relationships, custom SQL — run with dbt test in CI.
- Docs & lineage: dbt docs generates a dependency graph the whole team reads.
A typical dbt project lineage.
Key commands
dbt run --select +fct_order_linesBuild the model and all its upstream dependencies.
dbt testRun every declared test across the project.
dbt docs generate && dbt docs serveRender lineage and model docs for the team.
dbt buildRun seed/run/test in the right dependency order with failure isolation.
Examples
Model + test in one file set
-- models/marts/fct_order_lines.sqlSELECT o.order_id, o.customer_id, o.sold_at, sum(oi.qty) AS items_qty, sum(oi.gross_amount) AS gross_amountFROM {{ ref('stg_orders') }} oJOIN {{ ref('stg_order_items') }} oi USING (order_id)GROUP BY 1, 2, 3 -- schema.ymlversion: 2models: - name: fct_order_lines columns: - name: order_id tests: [unique, not_null]ref() resolves the true deps; schema.yml declares the tests that run in CI.
In production
- A 300-model project builds marts nightly; CI runs dbt test on every PR.
- A team lands raw partitions in the lake and dbt coupons incremental models over them.
- Source definitions feed 'freshness' checks so a quiet source table alerts before it bites.
Best practices
- Staging models should only clean/type; business logic lives in intermediates/marts.
- Every mart gets a unique/not_null test on its grain keys.
- Materialize incrementally only when the grain demands it; tables/views are simpler.
- Review dbt artifacts (manifest, run_results) in CI to enforce lineage constraints.
Common mistakes
- Pasting {{ ref() }} output into other models instead of ref()-ing it (the dependency lie).
- Building business logic in staging, making the 'clean' layer untrustworthy.
- No tests, so a schema drift sails silently into marts.
Debugging tips
- A model that 'changed but ran upstream' usually means a ref() mismatch or an un-invalidated cache.
- Use dbt ls --resource-type model and --select 'model:' filters to trace lineage issues.
- When a stage test fails, diff the raw source vs stg SELECT to find the transformation bug.
Interview checkpoint
Hands-on lab
- Write SQL models with ref() placeholders
- Resolve refs and execute models in dependency order on DuckDB
- Run simple unique/not_null tests against the output
Build a Mini-dbt: ref() Resolution, Order, and Tests
40 min · intermediate
Quick quiz
ref() models declare dependencies so dbt builds models in the correct order.
dbt is best described as…