Etl Elt Updated: 2026-01-21

What is dbt (Data Build Tool)?

An open-source transformation tool that enables data analysts and engineers to transform data in their warehouse using SQL and software engineering best practices.

From the field

dbt (data build tool) changed analytics engineering the way React changed frontend — it didn't invent the primitives but it packaged them into a workflow that actually worked. Before dbt, SQL transformations lived in stored procedures, Airflow BashOperators running psql files, BI-tool "custom SQL" fields, or personal notebooks — fragmented, untested, undocumented. dbt's move was deceptively simple: treat SQL models as version-controlled code, compile them into a DAG based on {{ ref() }} dependencies, run them in order, and wrap tests + docs around the whole thing. That one conceptual shift turned analysts into analytics engineers and made dbt the de-facto transformation tool in the modern data stack.

What dbt actually is on day-to-day

Day-to-day, dbt is: a folder of .sql files (each a SELECT statement), a dbt_project.yml, a profile config pointing to your warehouse, and the dbt CLI. You run dbt build; it compiles each model, figures out dependencies from ref() calls, materializes models as tables/views/incrementals in the order the DAG demands, runs tests, and produces run results. That's it. No orchestration, no scheduling (use Airflow/Dagster/dbt Cloud), no ingestion (use Fivetran/Airbyte). The narrow scope is the strength — dbt does one thing well.

Field story

A 40-person analytics team at a retail client had 800 models in BigQuery .sql files scattered across a Confluence wiki, with transformations duplicated across dashboards, no tests, and a weekly cadence where the "real" numbers diverged from the "approximate" numbers shown on a Monday morning. We migrated to dbt over three months: consolidated duplicated logic into reusable staging + intermediate + mart layers, added ~2000 tests (unique, not_null, relationships, accepted_values), and wired CI to run dbt build --select state:modified+ on every PR. Ramp time for new analysts dropped from 6 weeks to 1 week (docs site was the biggest reason), three production data bugs were caught by tests in the first month, and dashboard-divergence incidents went to zero because everyone queried the same marts. dbt isn't magic; it's just software engineering applied to SQL. That's enough.

dbt (data build tool) is an open-source command-line tool that enables data analysts and engineers to transform data in their data warehouse more effectively. It brings software engineering best practices like version control, testing, and documentation to analytics workflows.

What Makes dbt Different

Traditional ETL tools require you to write transformations in proprietary languages or drag-and-drop interfaces. dbt takes a different approach:

  • SQL-first: Write transformations in pure SQL (or Python in dbt Core 1.3+)
  • ELT, not ETL: Assumes data is already loaded; focuses only on the T (transform)
  • Modular: Build reusable models that reference each other
  • Version Controlled: Store all code in Git for collaboration
  • Tested: Define tests to validate data quality
  • Documented: Auto-generate documentation from your code

Core Concepts

  1. Models: SQL files that define transformations. Each model compiles to a SELECT statement and creates a table or view.
  1. Sources: Declare raw tables loaded by your ingestion tools (Fivetran, Airbyte, etc.)
  1. Tests: Assertions about your data (unique, not null, relationships)
  1. Documentation: Describe models and columns; dbt generates a searchable doc site
  1. Macros: Reusable SQL snippets (like functions) using Jinja templating

dbt Core vs dbt Cloud

  • dbt Core: Free, open-source CLI tool you run locally or in CI/CD
  • dbt Cloud: Managed platform with IDE, scheduling, and collaboration features

Common dbt Commands

dbt run          # Execute all models
dbt test         # Run all tests
dbt docs generate  # Generate documentation
dbt build        # Run + test in dependency order

Why Teams Adopt dbt

  • Speed: Analysts can own transformations without waiting for engineers
  • Quality: Tests catch data issues before they reach dashboards
  • Collaboration: Git-based workflow enables code review and teamwork
  • Maintainability: Modular, documented code is easier to understand and update

Key Points

Gotchas

dbt pitfalls I see at almost every client:

  • Overusing view materialization on frequently-queried models. Views re-execute the full SQL on every query; for a model sitting under a dashboard, that's dozens of identical recomputations per hour. Materialize as table or incremental for anything queried often. The inverse mistake — materializing everything as table — wastes compute rebuilding cold models.
  • Incremental models that lose late-arriving data. The default is_incremental() filter (WHERE updated_at > (SELECT MAX(updated_at) FROM {{ this }})) silently misses rows where updated_at is upstream-of-watermark. Use a lookback window (> MAX(updated_at) - interval '3 days') and a merge strategy that handles duplicates.
  • {{ ref() }} vs {{ source() }} confusion. ref points to another dbt model; source points to raw ingested tables. Using the wrong one breaks the DAG or creates hardcoded table names. Always source() at the staging layer, always ref() afterwards.
  • Tests that always pass. unique on an empty table passes. not_null on an all-null column returns zero failures if the column is added but nothing populates it. Combine tests with expect_table_row_count_to_be_between (via dbt-expectations package) to fail-fast.
  • SELECT * in staging models. Feels convenient; means that when a source adds a new PII column, it silently flows downstream without review. Explicit column lists in staging layers force deliberate schema changes.
  • Running dbt run without dbt test. dbt build does both in dependency order and stops on test failure, preventing downstream corruption. Using dbt run alone means a broken model's downstreams get rebuilt with bad inputs.
  • Jinja macro overuse. It's tempting to abstract everything. Teams end up with {{ my_custom_macro(table, schema, custom_arg, another_arg) }} where a regular SELECT would be clearer. Use macros for genuine DRY wins (pivot helpers, surrogate keys, config-aware filters), not for abstraction's sake.
  • Missing on_schema_change on incremental models. Default is ignore; adding a column to an incremental model doesn't backfill it. Set on_schema_change='sync_all_columns' or handle full refreshes explicitly.
  • State-based CI without baseline refresh. dbt build --select state:modified+ is great until the baseline manifest in CI is stale and the "modified" selector picks up everything. Refresh state periodically; document the expected failure mode.
  • Docs without ownership metadata. dbt docs are a fantastic wiki, but without an owner meta on every model, nobody knows who to call. Add meta: {owner: team-name} as a convention from day one.

Frequently Asked Questions

What is dbt used for?

dbt is used for transforming raw data in a cloud data warehouse into analytics-ready datasets — the T in ELT. You write modular SQL SELECT statements (models) that reference each other; dbt compiles them into a DAG, executes them in dependency order, runs data tests (unique, not_null, relationships, accepted_values, custom SQL), and auto-generates documentation. Common use cases: building a star-schema mart layer, consolidating logic across dashboards, enforcing data contracts, and making analytics changes reviewable via pull requests. dbt has become the de-facto transformation tool in the modern data stack precisely because it's narrow — it doesn't do ingestion or scheduling; it just does transformation well.

Is dbt ETL or ELT?

dbt is the T (Transform) component of an ELT pipeline. It assumes data has already been Extracted and Loaded into your warehouse by other tools (Fivetran, Airbyte, Stitch, Kafka Connect, custom scripts). dbt then performs the transformations inside the warehouse, leveraging the warehouse's compute (Snowflake, BigQuery, Redshift, Databricks). This ELT pattern is vastly more efficient than legacy ETL for cloud warehouses because transformation happens on the same infrastructure as storage — no data movement, no separate transform server. The ETL-vs-ELT war is essentially over; ELT won for cloud warehouses, and dbt won the transformation layer.

Is dbt free to use?

dbt Core is 100% free and open-source (Apache 2.0). You install the CLI, configure a `profiles.yml` with warehouse credentials, and run `dbt build` — no paid license. dbt Cloud is a commercial SaaS from dbt Labs offering a hosted IDE, job scheduling, CI integration, semantic layer, mesh, and enterprise features. Dbt Cloud has a free Developer tier (1 user), paid Team and Enterprise tiers. Many teams run dbt Core indefinitely, orchestrating with Airflow or GitHub Actions; others adopt dbt Cloud for the integrated IDE and managed scheduling. Both produce identical results because they're the same underlying engine.

What databases does dbt support?

First-party adapters: Snowflake, BigQuery, Redshift, Databricks, Postgres, and Apache Spark — maintained by dbt Labs. Community adapters add: DuckDB, MotherDuck, Trino/Presto, ClickHouse, SQL Server, Oracle, Teradata, Firebolt, Materialize, SingleStore, and more. Each adapter translates dbt's Jinja+SQL into warehouse-specific syntax. Feature support varies (Snowflake has the richest coverage; smaller warehouses may lack incremental materialization or specific macros). For any mainstream cloud warehouse, dbt works; for niche databases, check the adapter's GitHub for open issues before committing.

What are dbt models, tests, and sources?

Models are SQL files (one per transformation) that compile to `CREATE TABLE AS SELECT` or `CREATE VIEW AS SELECT` — the fundamental unit of work. Sources are YAML declarations of raw ingested tables (`source('raw', 'orders')`) with metadata and tests; they're the upstream boundary of your DAG. Tests are assertions: built-in (`unique`, `not_null`, `relationships`, `accepted_values`) or custom SQL returning zero rows on success. Additional key concepts: Seeds (CSV files loaded as tables), Snapshots (SCD-Type-2 history tracking), Macros (Jinja-based reusable SQL), Packages (shared macro libraries like dbt_utils), and Exposures (downstream consumers like dashboards for lineage tracking).

What is the difference between dbt Core and dbt Cloud?

dbt Core is the open-source CLI — you install it, configure `profiles.yml` with warehouse credentials, and run commands locally or in any orchestrator (Airflow, Dagster, GitHub Actions, cron). dbt Cloud is a managed SaaS wrapping dbt Core with: a browser-based IDE, Git integration, job scheduling, notifications, CI hooks, the Semantic Layer, dbt Mesh (cross-project refs), Explorer (enhanced docs), and enterprise SSO/RBAC. Both execute the same dbt Core underneath; dbt Cloud's value is the managed infrastructure and UX on top. Small teams often start with dbt Cloud's Developer tier or Airflow + dbt Core; enterprise teams typically use dbt Cloud for governance and semantic layer.

How should I structure a dbt project?

The widely-adopted convention (from dbt Labs' best-practices guide): `models/staging/` — 1:1 with source tables, light cleaning (renames, casts, no joins); `models/intermediate/` — joins and logic that doesn't yet produce a business entity; `models/marts/` — final fact and dimension tables consumed by BI, organized by business domain (`marts/finance/`, `marts/marketing/`). Tests live in `_schema.yml` files alongside models. Sources defined in `_sources.yml`. Macros in `macros/`. Seeds (reference CSVs) in `seeds/`. Snapshots (SCD2) in `snapshots/`. This layering keeps changes contained: source changes ripple through staging first; mart changes don't require staging edits.

Related Terms

SnowflakeData WarehouseETL (Extract, Transform, Load)Data Modeling

Related Tools

SnowflakeBigQueryRedshiftFivetranAirbyte

Learn More

Related Articles

Browse All Terms Read Articles