1. What dbt is, and why it exists
dbt · 1. What dbt is, and why it existsThree guides on this site already point here. Spark’s chapter 15 says “if your ETL is a chain of SQL transformations, dbt over a warehouse is a better tool than Spark.” Airflow’s chapter 15 says dbt is a complement rather than a competitor. Flink’s chapter 1 lists batch ETL as something that moved away to “Spark, a warehouse, dbt.”
This is the guide about what they are all pointing at.
The one idea
Section titled “The one idea”dbt is what happens when you decide that a SQL transformation is a piece of software, and should be treated like one.
That is the whole thing. Everything else in this guide follows from it.
Here is the world before:
THE STORED PROCEDURE ERA
a .sql file on someone's laptop a scheduled job in the warehouse UI a stored procedure nobody will touch a chain of CREATE TABLE AS statements run in an order somebody remembers
→ no version control → no tests → no idea what depends on what → no way to run it on a copy → the person who wrote it has leftAnd the claim dbt makes:
THE ANALYTICS ENGINEERING ERA
a model is a SELECT statement in a .sql file in git, reviewed in a pull request with tests that run in CI with dependencies dbt works out itself built into dev, staging or prod by config
→ the same discipline software has had for thirty years, applied to SQLNothing in the right-hand column is a new idea. Version control, testing, code review, dependency resolution, environments — software engineering had all of it. dbt’s contribution was noticing that the people writing SQL transformations had none of it, and building the smallest possible tool that gave it to them.
What a model actually is
Section titled “What a model actually is”This is the part people get wrong on first contact, so it is worth being blunt.
A dbt model is a SELECT statement. That is all it is. You do not write CREATE TABLE. You do not write INSERT. You do not write a schema. You write the query that produces the rows you want, and dbt wraps it in the DDL needed to persist it.
-- models/marts/customer_orders.sql
select c.customer_id, c.customer_name, count(o.order_id) as order_count, sum(o.amount) as lifetime_valuefrom {{ ref('stg_customers') }} as cleft join {{ ref('stg_orders') }} as o on c.customer_id = o.customer_idgroup by 1, 2That file becomes a table or a view in your warehouse called customer_orders. dbt writes the create table customer_orders as (...) around it.
The one piece of non-SQL is {{ ref('stg_customers') }}. It is a function call that returns the fully-qualified name of another model — analytics.dbt_sarah.stg_customers in your development schema, analytics.prod.stg_customers in production. That single function is where most of dbt’s value comes from, and chapter 3 is about why.
The T in ELT
Section titled “The T in ELT”dbt does one letter of ETL. Knowing which one saves a lot of confusion.
E EXTRACT pull data out of the source → Fivetran, Airbyte, Debezium, a Python script
L LOAD land it raw in the warehouse → the same tools
T TRANSFORM turn raw tables into tables people can actually query → dbtdbt does not move data. It has no connectors to Salesforce or Postgres-as-a-source. It cannot read a CSV off S3 and load it. It assumes the data is already in your warehouse, and it turns it into better-shaped data in the same warehouse.
This is the ELT pattern rather than ETL: load first, transform afterwards, in the warehouse, in SQL. That ordering only became sensible when warehouses got cheap and fast enough to do the transformation work — which is to say, when Snowflake, BigQuery and Redshift arrived. dbt is a product of that shift and could not have existed before it.
dbt does not have a compute engine
Section titled “dbt does not have a compute engine”Worth stating on its own, because it surprises people.
dbt run │ │ 1. read your .sql files │ 2. render the Jinja │ 3. work out the dependency order │ 4. wrap each model in DDL │ 5. send SQL over a connection ▼ ┌───────────────────────────────┐ │ YOUR WAREHOUSE │ │ Snowflake / BigQuery / │ │ Databricks / Postgres / ... │ │ │ │ ← all the actual work │ │ happens here │ └───────────────────────────────┘dbt is a code generator and a scheduler of statements. The data never passes through it. Your laptop running dbt run is sending text over a database connection and waiting.
Three consequences that matter:
- Your warehouse bill, not your dbt bill, is the cost of a dbt project. Chapter 13 is largely about this.
- dbt is as fast as your warehouse. Optimising a slow model means optimising SQL, not tuning dbt.
- dbt runs anywhere — a laptop, a container, a CI runner. It needs nothing but network access and credentials.
Where it came from
Section titled “Where it came from”- 2016 — Fishtown Analytics, a consultancy, builds an internal tool to stop rewriting the same transformation scaffolding for every client. Tristan Handy is the name usually attached.
- 2018 — open-sourced as dbt. Apache 2.0. It spreads by word of mouth through analytics teams who all have the same problem.
- 2019–2021 — dbt Cloud arrives; Fishtown Analytics becomes dbt Labs. The term “analytics engineer” is coined for the role the tool creates — someone between the data engineer and the analyst, who owns the transformation layer.
- December 2021 — dbt Core 1.0.
- 2023 — the Transform acquisition brings MetricFlow and the Semantic Layer.
- 2024–2025 — dbt Core 1.8 splits adapters out and adds unit tests; 1.9 adds the
microbatchincremental strategy and YAML snapshots. - 2025 — dbt Labs acquires SDF Labs and announces the Fusion engine, a Rust rewrite that actually parses and understands SQL rather than treating it as text. A merger with Fivetran was also announced during 2025.
The last point needs a caveat. Fusion and the Fivetran merger were both moving quickly as this was written, and the details — which features land in Core, what stays in Cloud, how licensing settles — are the kind of thing that changes. Check dbt’s own docs for the current state before making a decision that depends on it. Everything else in this guide is about the model that has been stable since 1.0.
The four things dbt gives you
Section titled “The four things dbt gives you”Strip away the features and it is these:
1. A dependency graph you did not have to write. You write ref(); dbt builds the DAG and runs models in the right order, in parallel where it can. Nobody maintains a list of which table comes first.
2. Environments for free. The same code builds into your personal dev schema, a CI schema, or production, because model names are resolved at compile time rather than hard-coded.
3. Tests that run on data, not just code. unique, not_null, relationships and accepted_values are one line of YAML each, and they run as SQL against real rows.
4. Documentation and lineage that are generated, not written. Because dbt knows the graph, it can draw it. Because it reads the warehouse catalogue, it knows the columns.
What dbt is not
Section titled “What dbt is not”An honest list, because half the disappointment with dbt comes from expecting one of these:
✗ NOT an orchestrator it has no scheduler, no retries across runs, no calendar, no alerting → Airflow, Dagster, Prefect, dbt Cloud jobs
✗ NOT an ingestion tool it cannot pull data from anywhere → Fivetran, Airbyte, Debezium, custom code
✗ NOT a compute engine the warehouse does everything → Snowflake, BigQuery, Databricks
✗ NOT a BI tool it builds tables; it draws nothing → Looker, Tableau, Metabase, Superset
✗ NOT for streaming dbt is batch. every run is a full re-evaluation of a graph → Flink, Materialize
✗ NOT a general programming environment Jinja is templating, not a language you want business logic inThe orchestration one is the most common confusion, so to be explicit: dbt run is a command. Something has to decide to run it. In practice that something is Airflow, Dagster, a dbt Cloud job, or a GitHub Action. Airflow’s chapter 15 describes the standard shape — Airflow around the outside, dbt in the middle.
Who it is for
Section titled “Who it is for”dbt fits when most of your transformation logic is expressible as SQL over tables that are already in one warehouse.
That is a real constraint and it excludes plenty. If your logic needs a machine-learning model, arbitrary Python over unstructured text, or data that lives in six systems that never meet, dbt is the wrong shape. Spark’s chapter 15 draws that line from the other side.
But the category dbt fits is enormous — it is most of what analytics teams do — and inside it dbt is close to unopposed. The realistic alternatives in 2026 are SQLMesh, your warehouse’s native tooling, or writing the scaffolding yourself. Chapter 15 takes each of them seriously.
What to take from this chapter
Section titled “What to take from this chapter”- dbt’s single idea is that a SQL transformation is software and deserves version control, tests, review and dependency management. Every feature follows from that.
- A model is a
SELECT. dbt writes the DDL around it. You never writeCREATE TABLE. ref()is the load-bearing feature — it builds the dependency graph and makes environments work, from one function call.- dbt is the T in ELT. It does not extract, does not load, and assumes the data is already in your warehouse.
- dbt has no compute engine. It generates SQL and sends it over a connection; the warehouse does all the work, and the warehouse bill is the cost of the project.
- It came out of a consultancy’s internal tooling in 2016, went open source in 2018, hit 1.0 in December 2021, and created the “analytics engineer” role along the way.
- The Fusion engine and the Fivetran merger were both in motion during 2025 — check current docs rather than trusting any write-up, including this one, on those specifics.
- It is not an orchestrator, not ingestion, not compute, not BI, and not streaming. Something else must decide to run it.
Next: the project — dbt_project.yml, profiles.yml, the directory layout, and how dbt finds a warehouse to talk to.