Python · SQL · Web Dev · Java · AI/ML tracks launching soon — your one platform for all of IT

What is dbt?

What dbt actually is, the ETL-to-ELT shift that created the need for it, its exact scope boundary against ingestion tools, why it exists, and how it compares to hand-rolled SQL, GUI ETL tools, and Dataform.

60 min September 2026
// Part 01 — The Transformation Layer

What a "Transformation Layer" Even Is

Before you can understand what dbt is, you need to understand the gap in a data stack that it fills. Picture a modern company — call it a mid-size grocery delivery startup, FreshCart. FreshCart has a production Postgres database recording every order, a Salesforce instance tracking sales leads, a Stripe account processing payments, and a mobile app sending click events. None of that data lives together, none of it is shaped for answering business questions, and most of it is structured for the needs of the application that produced it, not for analysis.

Getting from "data exists somewhere, in whatever shape the source system produced it" to "a clean table an analyst can query to answer 'what was our revenue by region last month'" requires three distinct kinds of work. First, the data has to be extracted from each source system. Second, it has to be loaded somewhere central — a data warehouse. Third, it has to be transformed: cleaned, renamed, joined across sources, deduplicated, aggregated, and reshaped into tables that actually answer business questions rather than just mirroring whatever an application's internal database schema happened to look like.

Beginner model: "Data engineering" is one big undifferentiated blob of moving data around.

Production model: a modern data stack is layered — an ingestion layer extracts and loads raw data, a transformation layer reshapes it into analytics-ready tables, and a business intelligence layer (Looker, Tableau, Mode) sits on top querying those finished tables. dbt's entire job is that middle layer, and nothing else.

That transformation layer is not a nice-to-have step you could skip. Raw data straight from a production database is usually unusable directly for analysis: it might have technical column names like usr_stat_cd instead of user_status, it might spread related facts across a dozen normalized tables that need joining, it might contain soft-deleted rows that should be filtered out, duplicate records from a flaky sync process, or nested JSON blobs that need to be flattened into columns. Someone — or something — has to do that reshaping work before a business question can be answered reliably. That "someone" is the transformation layer, and dbt is the tool built specifically to be that layer.

LayerJobTypical tools
Ingestion (extract + load)Pull data out of source systems and land it into the warehouse, largely as-is.Fivetran, Airbyte, Stitch, custom scripts
TransformationClean, join, dedupe, and aggregate raw warehouse tables into analytics-ready models.dbt, Dataform, hand-written SQL
Consumption / BIQuery the finished, transformed tables to build dashboards and reports.Looker, Tableau, Mode, Metabase
Why this matters before anything else
Almost every confusion beginners have about dbt traces back to not having this three-layer picture clearly in mind. If you remember one thing from this Part, remember that dbt lives in the middle layer only — it never touches the first layer (extraction/loading) and it is not the third layer (dashboards). Part 03 makes this scope boundary explicit and unambiguous.

A concrete before-and-after, in one table

It helps to see the raw-versus-transformed contrast side by side, using the same FreshCart orders data. The left column below is roughly what a row looks like fresh out of the production database. The right column is what an analyst actually wants to query.

raw row, as landed by ingestion (one row from raw.freshcart_raw.orders)
ord_id | cust_id | ord_stat | ord_placed_ts          | amt_cents | is_deleted
78341  | 9042    | 3        | 2026-03-11T14:02:33Z   | 4599      | false
transformed row, after dbt's staging model (analytics.stg_orders)
order_id | customer_id | order_status | order_placed_at      | amount_usd
78341    | 9042        | completed    | 2026-03-11 14:02:33  | 45.99

Notice everything that changed between the two: cryptic column names became readable ones, a numeric status code (3) became a human-readable label (completed), cents became dollars, and the soft-deleted flag was used to filter the row rather than exposed as a column an analyst has to remember to check every time. None of that is exotic engineering — it's exactly the kind of mechanical, repetitive cleanup that used to live scattered across dozens of individual analysts' personal SQL scripts before a shared transformation layer existed.

// Part 02 — ETL vs ELT

ETL vs ELT — The Shift That Created the Need for dbt

If you take away only one piece of context from this entire module, make it this one: dbt exists because the data industry moved from ETL to ELT, and dbt is purpose-built to be the "T" in that new order. Understanding why that shift happened tells you why dbt looks the way it does.

ETL — transform before loading

For a long time, the standard architecture was extract, transform, load. Data was extracted from source systems, piped through a separate transformation engine or ETL tool (Informatica, Talend, SSIS) running on its own dedicated servers, and only the final, cleaned, business-ready tables were loaded into the data warehouse. The warehouse itself was treated as a precious, expensive resource — on-premises warehouses had fixed hardware, licensing costs tied to server capacity, and limited, hard-to-scale compute. You did not want to waste that scarce warehouse compute on the "dirty work" of transformation; you did that work elsewhere and only loaded the finished product.

ELT — load first, transform where the data lives

Cloud data warehouses changed the economics entirely. Snowflake, BigQuery, and Redshift decoupled storage from compute and made compute elastic — you can spin up more processing power for the duration of a transformation job and pay only for what you used, rather than provisioning a fixed server for peak load year-round. Storage itself also became cheap enough that landing raw, unprocessed data and keeping it around indefinitely was no longer wasteful.

Once compute is cheap, elastic, and lives inside the warehouse itself, there is no longer a reason to transform data on a separate system before loading it. It becomes simpler and more flexible to load raw data into the warehouse immediately after extraction, and then run transformations as SQL queries against the warehouse's own compute — extract, load, transform. This has real practical advantages beyond just cost: raw data is preserved in the warehouse in its original form, so if a transformation turns out to be wrong, you can fix the SQL and simply re-run it against the still-intact raw data, rather than having to re-extract from the source system.

ETL vs ELT — where transformation compute happens
ETL (older model):
  source system --extract--> ETL server/tool --transform--> warehouse
                                    ^
                            transformation happens HERE,
                            on a separate system, before loading

ELT (current model, what dbt is built for):
  source system --extract--> warehouse (raw data lands as-is)
                                  |
                                  v
                          dbt runs SQL transformations
                          HERE, inside the warehouse itself,
                          using the warehouse's own compute
ETLELT
Where transformation runsA separate processing engine, before the warehouseInside the warehouse, using its own compute
When it made senseOn-premise warehouses with fixed, expensive computeCloud warehouses with cheap, elastic compute
Raw data preserved?Often not — only the transformed output is keptYes — raw data lands and stays, transformations are re-runnable
Where dbt fitsDoes not apply — dbt has nothing to plug intoThis is exactly the "T" step dbt is built to run
Common confusion
Do not think of ETL and ELT as two names for the same process. They describe genuinely different architectures with different tools at each step, and the shift from one to the other — driven by cheap warehouse compute — is the direct reason a tool like dbt could exist at all. Without cheap, elastic warehouse compute, running heavy SQL transformations directly in the warehouse the way dbt does would not have been practical or affordable.

Why "transform where the data already lives" is not just a cost story

The cost argument is the headline reason ELT became viable, but there is a second, quieter benefit that turned out to matter just as much in practice: keeping raw data around, untouched, inside the warehouse. Under classic ETL, once the transformation server produced its final output and that intermediate raw data was discarded or archived elsewhere, discovering that a transformation step three years ago dropped a column you now need meant re-extracting from the source system — which might no longer even exist in the same form, or at all.

Under ELT, the raw freshcart_raw.orders table sits in the warehouse indefinitely (or for as long as retention policy allows), completely unaffected by any bug in a dbt model built on top of it. If a staging model turns out to have miscalculated a column, you fix the SQL and simply re-run dbt — the raw source data needed to recompute the correct answer never went anywhere. This re-runnability is a direct, practical consequence of the ELT ordering, not a separate feature dbt had to build.

QuestionUnder ETLUnder ELT (dbt's world)
If a transformation bug is found later, can you recompute from scratch?Only if the raw extract was separately archived — often it wasn'tYes — raw data is sitting right there in the warehouse
Where does transformation compute get billed?A separately provisioned ETL server, sized for peak load year-roundThe warehouse itself, scaled elastically only while a job runs
Can a new use case reuse the same raw data differently?Only by re-extracting, since the raw form usually wasn't keptYes — write a new model against the same already-loaded raw table
// Part 03 — What dbt Actually Is

What dbt Actually Is, and Its Exact Scope Boundary

At its core, dbt is a command-line tool and framework that lets you write data transformations as SQL SELECT statements — called models — and handles everything around turning those SELECT statements into real tables and views in your warehouse: figuring out the order to run them in based on their dependencies, running automated data quality tests against them, and generating documentation and a visual dependency graph from the same codebase.

Concretely, a dbt model is a single .sql file containing one SELECT statement. You do not write CREATE TABLE or INSERT statements yourself — dbt wraps your SELECT in the appropriate DDL/DML automatically, based on how you've configured that model (typically as a table or a view). You reference other models not by hardcoding a schema and table name, but through a function called ref(), which dbt resolves at compile time and uses to build a dependency graph automatically — covered in full mechanical detail in the next module.

a minimal dbt model — models/stg_orders.sql
select
    order_id,
    customer_id,
    order_status,
    cast(order_placed_at as timestamp) as order_placed_at,
    amount_cents / 100.0 as amount_usd
from {{ source('freshcart_raw', 'orders') }}
where order_status is not null

Running dbt run takes that file, resolves the source() call into the actual raw table name, and executes the equivalent of CREATE OR REPLACE VIEW stg_orders AS <that SELECT> against your warehouse. That is the entire mechanic, at a beginner level — the deep mechanics of compilation and execution order are the subject of Module 02.

The critical scope boundary: dbt does not extract or load data

This is the single most common misunderstanding beginners bring to dbt, so it's worth stating as plainly as possible: dbt has no functionality whatsoever for connecting to source systems like a production application database, a SaaS API, or a file drop. It cannot pull data out of Postgres, Salesforce, or Stripe. Its only interaction with the world is running SQL against a warehouse it is already connected to, on data that is already sitting there.

What dbt does:

  • Compiles SQL models (resolving Jinja templating and ref()/source() calls)
  • Determines the order to run models in, based on their dependencies
  • Executes compiled SQL against the warehouse to create/update tables and views
  • Runs automated data quality tests against those tables
  • Generates documentation and a visual dependency graph from the project

What dbt does NOT do:

  • Extract data from source systems (production databases, SaaS APIs, files)
  • Load raw data into the warehouse for the first time
  • Store any data itself — everything lives in the warehouse, not in dbt
  • Serve as a BI or dashboarding tool for end users
Where dbt sits in a real stack
A typical stack looks like: Fivetran extracts raw orders, customers, and payments tables from FreshCart's production Postgres database and lands them into a raw schema in Snowflake, on a schedule, completely independent of dbt. dbt then reads those raw tables and builds staging, intermediate, and mart models on top of them. Looker then queries the finished mart tables to power dashboards. dbt only ever touches the middle step.

A quick self-check: is this dbt's job or not?

Because the scope boundary is so frequently misunderstood, it's worth running through a few concrete requests and sorting which layer each one actually belongs to.

RequestIs this dbt's job?Why
"Pull new rows from our Salesforce account every hour."NoThis is extraction — an ingestion tool's job, not dbt's
"Join orders and customers into one clean revenue table."YesThis is transformation of data already in the warehouse — exactly dbt's job
"Build a dashboard showing revenue by region."NoThis is consumption/BI — a tool like Looker or Tableau queries dbt's finished output
"Make sure order_id is always unique in our orders table."YesThis is a data quality test, one of dbt's core features (Part 04)
"Load a CSV of sales territory mappings maintained by hand."Partiallydbt can load small, static reference files as "seeds," but this is not its role for large or frequently-changing raw data
// Part 04 — Models, Tests, and Docs

The Three Pillars: Models, Tests, and Documentation

dbt's value comes from bundling three things that used to live separately (if they existed at all) into one workflow built around the same codebase: the transformation logic itself, automated checks on that logic's output, and documentation describing what everything means.

Models — SQL as version-controlled code

A model is just a SELECT statement saved as a .sql file inside a dbt project, which is itself an ordinary git repository. This single fact is what unlocks everything else: because your transformation logic is plain text in version control, you get pull requests, code review, diffs that show exactly what changed in a model, a full history of who changed what and why, and the ability to revert a bad change in seconds. None of that is possible when transformation logic lives as ad hoc scripts on someone's laptop or buried inside a GUI tool's proprietary project file.

Tests — catching bad data before it reaches a dashboard

dbt lets you declare tests against your models directly alongside the model definitions — for example, asserting that order_id is unique and never null, or that every customer_id in the orders table actually exists in the customers table. Running dbt test executes these as SQL queries that should return zero failing rows; if they return any rows, the test fails and you know immediately, rather than an analyst noticing a dashboard number looks wrong three weeks later.

a simple dbt test declaration — models/schema.yml
models:
  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - unique
          - not_null
      - name: customer_id
        tests:
          - not_null
          - relationships:
              to: ref('stg_customers')
              field: customer_id
output
$ dbt test
Running with dbt=1.8.0
Found 2 models, 3 tests

1 of 3 START test unique_stg_orders_order_id ................. [RUN]
1 of 3 PASS unique_stg_orders_order_id ....................... [PASS in 0.41s]
2 of 3 START test not_null_stg_orders_order_id ............... [RUN]
2 of 3 PASS not_null_stg_orders_order_id ..................... [PASS in 0.38s]
3 of 3 START test relationships_stg_orders_customer_id ....... [RUN]
3 of 3 FAIL 4 relationships_stg_orders_customer_id ........... [FAIL 4 in 0.52s]

Completed with 1 error and 0 warnings:
  Failure in test relationships_stg_orders_customer_id (models/schema.yml)
  Got 4 results, configured to fail if != 0

That failing test just caught four orders referencing a customer_id that doesn't exist in the customers table — a real data quality problem, caught automatically, before anyone built a dashboard on top of bad data.

Documentation — generated from the same source of truth

Because models, their columns, and their tests are all declared in the project, dbt can generate a searchable documentation site and a visual DAG (directed acyclic graph, covered in depth in Module 02) directly from that same code — no separate wiki to keep in sync, no diagram that goes stale the moment someone adds a model.

adding a description — models/schema.yml
models:
  - name: stg_orders
    description: >
      One row per order, cleaned and standardized from the raw
      orders table. Soft-deleted (test) orders are filtered out.
    columns:
      - name: order_id
        description: Primary key. Unique identifier for an order.
      - name: amount_usd
        description: Order total, converted from cents to dollars.

Running dbt docs generate followed by dbt docs serve reads all of this — model descriptions, column descriptions, declared tests, and the dependency graph built from ref() calls — and produces a browsable, searchable site automatically. Anyone on the team can look up what amount_usd means, which tests run against it, and which models depend on it, without asking the person who wrote it or hunting through old Slack threads.

The kinds of tests you'll actually reach for

dbt ships a small set of built-in "generic" tests that cover the overwhelming majority of everyday data quality checks, and the community and dbt Labs both maintain packages of additional ones for more specific situations.

TestWhat it checksExample use
uniqueNo duplicate values in a columnorder_id should never repeat
not_nullA column never contains a nullcustomer_id should always be populated
accepted_valuesA column only contains values from an allowed listorder_status should only be one of: pending, completed, cancelled, refunded
relationshipsEvery value in a column exists in a referenced column elsewhere (referential integrity)Every customer_id in orders should exist in the customers table

Beyond these built-ins, teams commonly write custom "singular" tests — a plain SQL query saved under a tests folder that should return zero rows if the data is healthy, for business rules too specific for a generic test to express, like "refunded orders should never have a positive revenue amount."

a custom singular test — tests/assert_refunds_are_never_positive.sql
-- This test fails if it returns any rows at all
select
    order_id,
    amount_usd
from {{ ref('stg_orders') }}
where order_status = 'refunded'
  and amount_usd > 0
// Part 05 — Why Companies Adopted dbt

Why Companies Adopted dbt — and the Rise of "Analytics Engineering"

Before tools like dbt existed, transformation logic tended to live in one of two uncomfortable places. Either it lived in data engineers' pipeline code — Python or Spark jobs maintained by people whose primary job was infrastructure, not business logic, creating a bottleneck whenever a business question needed a new metric or a new join — or it lived in individual analysts' personal SQL scripts, copy-pasted and slightly modified from person to person, with no shared version history, no tests, and no way to know if changing one query would break someone else's downstream report.

dbt gave the SQL transformation layer the same tooling that software engineers had taken for granted for years: version control, code review, automated testing, CI/CD, and documentation generated from code. That combination let a new discipline take shape — analytics engineering — sitting between data engineering and data analysis. An analytics engineer owns the transformation layer: turning raw ingested data into clean, tested, documented models that analysts and BI tools can trust and query directly.

RolePrimary focusTypical tools
Data engineerBuilding and maintaining pipelines, infrastructure, ingestionPython, Spark, Airflow, cloud infrastructure
Analytics engineerTransforming raw warehouse data into clean, tested, documented modelsdbt, SQL, git
Data analystAnswering business questions and building reports on top of clean modelsSQL, Looker, Tableau, Mode
Why this job title exists
"Analytics engineer" is not a rebrand of "data analyst who knows more SQL." It specifically reflects someone applying software-engineering discipline — version control, testing, modularity, CI — to the transformation layer. That discipline is only practical at scale because dbt (and tools like it) made it convenient and standard, rather than something each team had to invent for itself.

The business case companies made for adopting dbt typically comes down to trust and speed: trust, because tested and documented models catch data quality problems before they reach an executive dashboard; speed, because a well-organized dbt project with modular, reusable models lets a team answer a new business question by composing existing models rather than starting a transformation from scratch every time.

What changed day-to-day for the people doing the work

It's worth being concrete about what adopting dbt actually changes for an analyst or engineer's daily workflow, rather than leaving it abstract. Before, a typical change — say, fixing a miscalculated revenue metric — might mean finding whichever personal SQL script or dashboard query computed that metric, editing it in place, and hoping no one else's separate copy of similar logic needed the same fix. There was often no reliable way to know who else depended on that logic, and no record of why the number had changed if someone asked next quarter.

After adopting dbt, the same fix is made once, in one model, as a pull request. Anyone who depends on that model — tracked explicitly through ref(), as later modules cover — is automatically affected the next time the project runs, with no separate copies to hunt down. The change has a commit message, an author, a timestamp, and (ideally) a reviewer, forming a durable record of exactly what changed and why, which is precisely the kind of change-tracking software engineers have relied on for decades and which analytics work mostly lacked before this generation of tooling.

Aspect of the jobBefore a shared transformation layerAfter adopting dbt
Where logic livesScattered across personal scripts, BI tool queries, ad hoc notebooksOne version-controlled project, in git
Reviewing a changeRarely happened — often no formal review step existedPull request review, same as application code
Knowing who depends on a queryUsually unknown — tribal knowledge, if anyone rememberedExplicit via the dependency graph (ref())
Catching a broken metricSomeone notices a dashboard number looks wrongAn automated test fails before the bad data reaches anyone
// Part 06 — Where the Name Comes From, and a Short History

Why "dbt" Stands for "Data Build Tool" — and a Short, Accurate History

dbt is short for "data build tool" — a deliberately literal, unglamorous name reflecting what the tool does: it builds your data (models) the way a build tool like Make or Maven builds software, resolving dependencies and running steps in the right order.

dbt was created inside a consulting company called Fishtown Analytics, founded by Tristan Handy and Drew Banin, who were doing analytics consulting work for clients and kept reinventing the same SQL-organization patterns project after project — dbt began as an internal tool to standardize that work before being open-sourced. As dbt grew into its own product and business, Fishtown Analytics renamed itself dbt Labs to reflect that dbt had become the company's core focus rather than a side tool supporting consulting work.

dbt Core vs dbt Cloud

dbt Core is the open-source command-line engine — the actual compiler and executor that reads your project, resolves Jinja and ref()/source() calls, builds the dependency graph, and runs SQL against your warehouse. It is free, runs anywhere you can run a terminal, and is what most of the mechanics in this track describe.

dbt Cloud is dbt Labs' commercial, hosted product built around that same dbt Core engine. It adds a browser-based IDE for writing and running models, a built-in job scheduler so you don't need to wire up your own cron or Airflow trigger just to run dbt on a schedule, hosted documentation, and collaboration features aimed at teams. The underlying transformation logic — the SQL, the compilation, the DAG — is the same engine either way; dbt Cloud is infrastructure and convenience wrapped around dbt Core, not a different transformation language or a different set of capabilities.

dbt Coredbt Cloud
What it isOpen-source CLI engineHosted product built on dbt Core
CostFreeCommercial, has a free tier for small teams
SchedulingYou wire up your own (cron, Airflow, CI)Built-in scheduler
Where you write modelsAny text editor / local machineBrowser-based IDE (or still locally, and sync via git)
Underlying transformation logicSame engineSame engine

A brief, accurate timeline

dbt's early versions in the mid-2010s were used internally at Fishtown Analytics for consulting engagements before being released as an open-source project. Adoption grew steadily through the later 2010s as more data teams moved onto cloud warehouses and needed exactly the kind of in-warehouse transformation tool dbt provided. dbt Labs later introduced dbt Cloud as a commercial product layered on top of the same open-source engine, and the company has continued to expand the core engine's capabilities (incremental models, more materializations, a growing package ecosystem) alongside dbt Cloud's own feature set.

Keep the history honest
It's easy to overstate or misremember specific dates and version numbers for a fast-moving open source project. The reliable facts worth holding onto are the ones in this Part: Fishtown Analytics created dbt, later renamed itself dbt Labs as dbt became the company's core product, dbt Core stayed open source throughout, and dbt Cloud is a hosted product built around that same open-source engine — not a rewrite or a different transformation model.
// Part 07 — dbt vs Alternatives

dbt vs. Its Alternatives — A Fair Comparison

dbt is not the only way to solve the transformation problem, and it's worth understanding its real alternatives honestly rather than assuming it's an obviously correct default in every situation.

Hand-written SQL scripts, no framework

The simplest possible approach: a folder of .sql files, run manually or via a basic cron job, with no dependency tracking, no built-in testing, and no documentation generation. This is genuinely fine for a very small team with a handful of models and low complexity. It stops scaling quickly once you have dozens of interdependent models, because nothing tracks which models depend on which, nothing warns you when a change to one query breaks a downstream one, and there's no standardized way to test data quality.

GUI-based ETL/ELT tools

Tools with drag-and-drop transformation canvases let you build pipelines visually rather than in SQL files. This can be more approachable for teams with less SQL fluency, and some of these tools bundle ingestion and transformation together. The trade-off is that the transformation logic usually lives inside the tool's own proprietary project format rather than as portable, plain-text SQL, which makes version control, code review, and migrating away from the tool harder.

Dataform — Google's similar product

Dataform is conceptually very close to dbt: SQL-based models, a dependency graph built from ref()-style references, and built-in testing. Google acquired Dataform and integrated it directly into BigQuery, so it has a natural advantage for teams already standardized on BigQuery. dbt is warehouse-agnostic and has a larger, longer-established open-source community and ecosystem of packages. Neither tool has a decisive technical advantage over the other for most use cases — the practical deciding factor is usually which warehouse a team is on and which tool the team already knows, not a meaningful gap in capability.

OptionStrengthTrade-off
Hand-written SQL scriptsZero learning curve, no new tool to adoptNo dependency tracking, testing, or docs at scale
GUI ETL/ELT toolApproachable for less SQL-fluent teamsLogic locked in a proprietary format, harder to version control
DataformTight native integration with BigQuerySmaller ecosystem outside the BigQuery/Google Cloud world
dbtWarehouse-agnostic, large ecosystem and communityStill requires SQL fluency and a modeling discipline to pay off

A realistic decision scenario

Consider a hypothetical 12-person data team at a mid-size subscription business, currently maintaining about 40 hand-written SQL scripts that build their reporting tables, run via a single cron job on a shared server. Two engineers recently left, and no one else fully understands the dependency order those 40 scripts need to run in — the order was tribal knowledge those two engineers carried in their heads. A change to script 12 recently broke script 31 downstream, and nobody noticed for two weeks because there was no test that would have caught it.

This is close to the textbook scenario dbt was built for: the team is already firmly on a cloud warehouse (so ELT compute economics apply), the transformation logic already exists as SQL (so the migration is organizational, not a rewrite into a new language), and the pain points — unclear dependency order, no tests, tribal knowledge walking out the door — are exactly what a dependency graph, automated tests, and version control solve. A team in this position adopting dbt is not choosing it over a meaningfully better alternative in the abstract; they're choosing it because their actual, concrete problems match what dbt is built to address.

Contrast that with a two-person startup with three simple reporting queries and no reporting complexity to speak of yet. Introducing dbt there has a real cost — a new tool, a new project structure, a new deployment step — for a problem (uncoordinated, poorly-tested SQL at scale) that doesn't exist yet at that size. This is exactly why "should we adopt dbt" is not a yes/no question with one universal answer — it depends on whether the team's actual pain points match what the tool solves, the same honest evaluation this Part has tried to walk through rather than assert.

Take away a fair picture, not a sales pitch
This module is not trying to convince you dbt is the only correct choice. It's trying to make sure you understand precisely what problem it solves (the transformation layer of ELT), what it doesn't do (extraction, loading, dashboarding), and where it sits relative to genuinely reasonable alternatives — so that when you do use it, you understand why.
// Part 08 — Who Actually Uses dbt, Day to Day

Who Actually Uses dbt, and What a Normal Day Looks Like

It helps to ground all of this in what using dbt actually looks like for the people who do it daily, rather than leaving the whole module at the level of architecture diagrams. The primary users of dbt are analytics engineers, though data analysts and data engineers both interact with dbt projects regularly too, just with different emphasis.

RoleHow they typically use dbt
Analytics engineerWrites and maintains the bulk of models, tests, and documentation; owns the overall project structure and conventions
Data analystOften writes simpler mart models for their own reporting needs, and consumes staging/intermediate models others built rather than starting from raw sources
Data engineerOwns the ingestion tools feeding raw data into the warehouse and the orchestration (Part 05 of Module 02) that triggers dbt runs, without necessarily writing many models themselves

A typical day for an analytics engineer working in dbt involves picking up a request — a new metric, a bug in an existing model, a data quality alert from a failed test — checking out a branch, editing or adding a .sql model file (and its accompanying YAML for tests and documentation), running dbt locally against a personal development schema to verify the change works and doesn't break downstream models, and opening a pull request for review before merging. None of this requires touching a warehouse console directly or writing raw DDL by hand — the whole workflow happens through SQL files and git, exactly the way software engineers work with application code.

This is the practical payoff of everything in this module
Every concept covered so far — ELT, the transformation-layer scope boundary, models/tests/docs, analytics engineering as a discipline — converges on this one everyday workflow: editing a SQL file in a code editor, running it locally, and merging it through a pull request, the same rhythm a backend engineer would recognize, applied to data transformations instead of application features.

What this means for how you should learn dbt from here

Because the daily workflow is fundamentally "edit a SQL file, run it, check the result," the most effective way to build real fluency with dbt is not to memorize every configuration option up front, but to get a small project running locally as early as possible and start making small, concrete changes to it — exactly the sequence the next module in this track walks through, moving from this conceptual foundation into the mechanical details of compilation, the dependency graph, and execution order.

// Misconceptions

Common Misconceptions About dbt

Five beliefs that trip up almost everyone new to dbt — each one traces back to a specific Part above where the correction is explained in full.

"dbt extracts data from our production databases and loads it into the warehouse."
dbt never touches source systems. It only runs SQL against data that is already sitting in your warehouse. Extraction and loading are a separate job, handled by tools like Fivetran, Airbyte, or a custom ingestion pipeline — see Part 03 for the exact scope boundary.
"dbt is a database. My tables live inside dbt."
dbt has no storage of its own. Every table and view dbt creates lives in your actual warehouse — Snowflake, BigQuery, Redshift, Databricks. dbt is a compiler and orchestrator that generates and runs SQL there. Delete your dbt project and your warehouse tables are untouched, as explained in Part 03.
"You need to learn a whole new proprietary query language to use dbt."
You write ordinary SQL SELECT statements. dbt adds a thin templating layer (Jinja) on top for referencing other models and reusing logic, but the core skill is still SQL you likely already know — covered in Part 04.
"ETL and ELT are basically the same thing, just different letters."
They describe two different architectures for where transformation happens — before loading (ETL) versus after loading (ELT) — and the shift from one to the other is the entire reason dbt exists. Part 02 walks through why this distinction matters.
"dbt Cloud and dbt Core are two totally different products with different capabilities."
dbt Core is the open-source command-line engine that does the actual compiling and running of models; dbt Cloud is a hosted product built around that same engine, adding a scheduler, UI, and hosted docs. The underlying transformation logic is the same — see Part 06.
// Real-World Stories

Why This Actually Matters — Three Real Scenarios

At Notion:

An analytics engineer joining Notion's data team inherits a warehouse where dozens of dashboards all define "active workspace" slightly differently — some count any workspace with a login in 30 days, others require at least one page edit. Without a shared transformation layer, each analyst had been writing their own version of that logic directly into their BI tool's queries. Migrating that logic into a single dbt model — dim_active_workspaces — with a tested, documented definition means every downstream dashboard references the same ref() and the same number, permanently ending the "whose number is right" debates in every metrics review.

At Faire:

A finance stakeholder at Faire asks for a new metric — gross merchandise volume by wholesale brand cohort — that requires joining order data, brand signup dates, and a currency conversion table. Because the raw orders and brands tables were already modeled as clean, tested dbt staging models from previous work, the analytics engineer builds the new metric as a mart model that references those existing staging models with ref(), rather than re-writing the underlying cleaning and joining logic from scratch. The new report ships in an afternoon instead of a week, because the transformation layer had already done the hard, reusable work once.

In a system design / analytics-engineering interview:

A candidate is asked to design a data platform for a hypothetical subscription business. A weak answer jumps straight to "we'd use dbt" without explaining why. A strong answer first lays out the ELT architecture — ingestion tool lands raw data, warehouse stores it cheaply, transformation layer builds it into analytics-ready tables — and only then names dbt specifically as the tool that fills the transformation layer, explaining that it would not replace the ingestion tool and would not itself power the dashboards. That framing (Part 01, Part 03) is exactly what interviewers are listening for — precise scope, not tool-name-dropping.

At Warby Parker:

A newly hired data analyst is asked why the company's "customer lifetime value" number differs between the finance team's spreadsheet and the marketing team's dashboard. Tracing both back, they find finance's number comes from a manually maintained SQL query last edited eight months ago, and marketing's comes from a different query built independently by a contractor who has since left. Neither definition is documented anywhere, and neither team knows the other's number exists. Consolidating both into a single dbt model — with the definition written once, tested, and documented — doesn't just fix the immediate discrepancy; it removes the possibility of the same drift happening again, because every downstream consumer now references the one model instead of maintaining its own copy of the logic.

// Interview Prep

Interview Questions You Should Be Able to Answer

1. What is dbt, and what problem does it solve that plain SQL scripts don't?

dbt (data build tool) is a command-line framework that lets analytics engineers write data transformations as version-controlled SQL SELECT statements, called models, and gives them dependency management, testing, and documentation on top. As Part 03 covers, dbt's actual job is narrow: it compiles your SQL (resolving Jinja templating like ref() and source()), figures out in what order models must run based on their dependencies, and executes each one against the warehouse as a CREATE TABLE AS SELECT or CREATE VIEW AS statement.

What it solves, compared to a folder of hand-run SQL scripts, is everything software engineers take for granted and analysts historically didn't have: your transformation logic lives in git, so you get code review, a commit history, and the ability to revert a bad change. You get automated tests that fail your pipeline if a primary key turns out to have duplicates or a foreign key points nowhere. You get documentation and a dependency graph generated from the code itself, not a wiki page that goes stale. Part 05 covers why this bundle of practices gave rise to the "analytics engineer" title as its own discipline.

2. Walk me through ETL versus ELT and why the order matters.

ETL — extract, transform, load — was the standard architecture when warehouses had fixed, expensive compute. You extracted from source systems, ran transformations on a separate processing server (or inside the ETL tool itself) to shape the data into its final form, and only then loaded the finished, clean tables into the warehouse. The transform step happened outside the warehouse because the warehouse's compute was too limited and too costly to spend on transformation work.

ELT — extract, load, transform — flips the last two steps. Raw data is loaded into the warehouse essentially as-is, immediately after extraction, and transformation happens afterward, inside the warehouse, using the warehouse's own compute. This became viable once cloud warehouses like Snowflake, BigQuery, and Redshift made compute cheap, elastic, and separately scalable from storage. Part 02 goes through this shift in detail — it's the single most important piece of context for why dbt exists at all: dbt is built specifically to be the "T" in ELT, run natively as SQL inside the warehouse rather than in some external transformation engine.

3. Does dbt replace tools like Fivetran or Airbyte?

No, and conflating them is one of the most common mistakes beginners make, which is why Part 03 spends real time on dbt's scope boundary. Fivetran and Airbyte are ingestion tools — they handle the "EL" of ELT, connecting to source systems (a production Postgres database, a Salesforce API, a Stripe account) and landing raw data into the warehouse. dbt starts only after that raw data is already there. It has no connectors to source systems and no concept of extraction.

In a real stack, these tools sit side by side: Fivetran lands raw stripe.charges and salesforce.opportunities tables into a raw schema on a schedule, and dbt then reads those raw tables and builds the cleaned, joined, aggregated models on top. Neither tool can do the other's job.

4. Why did the industry start using the term "analytics engineer," and how does it relate to dbt?

Before dbt, there was a gap between data engineers (who built pipelines and infrastructure, usually in Python/Scala/Spark, and often didn't want to own business logic) and data analysts (who wrote SQL to answer business questions but usually weren't given the tools or expectation to apply software engineering practices to that SQL — no version control, no tests, no CI). Transformation logic ended up either bottlenecked on data engineers who had bandwidth for pipeline work, not business logic, or scattered across analysts' personal scripts with no shared standard.

dbt gave the SQL-transformation layer its own tooling — version control, testing, documentation, modularity via ref() — which meant it could become its own discipline, distinct from both data engineering and analysis: analytics engineering, covered in Part 05. The job exists largely because dbt (and tools like it) made treating SQL with engineering discipline practical and expected, not because the underlying work itself is new.

5. A stakeholder asks you to "just connect dbt to our Salesforce instance." How do you respond?

This is a direct test of the scope boundary from Part 03. The accurate answer is that dbt cannot connect to Salesforce, or to any source system, at all — it has no extraction capability. What the stakeholder actually needs is an ingestion tool (Fivetran, Airbyte, or a similar connector-based product) configured to pull Salesforce data into the warehouse on a schedule; only once that raw data exists in the warehouse does it become something a dbt model could reference with source() and build on top of.

The stronger version of this answer doesn't just correct the misconception — it explains why the separation is useful: ingestion and transformation genuinely are different problems (one deals with authenticating to external APIs and handling their specific rate limits and schemas, the other deals with reshaping data that's already landed), and keeping them as separate tools means each can be swapped or scaled independently, exactly as Part 01's three-layer model lays out.

6. What are dbt's main alternatives, and when might a team choose something else?

Part 07 covers this landscape in more depth, but at a high level there are three categories. First, hand-rolled SQL scripts with no framework — the simplest option, viable for a very small team with few models, but it scales poorly: no dependency tracking, no tests, no documentation, and easy to silently break downstream tables when you change an upstream one. Second, GUI-based ETL/ELT tools that do transformation inside a drag-and-drop interface — approachable for less SQL-fluent teams, but the transformation logic is locked inside the tool's proprietary format, which makes version control, code review, and portability harder.

Third, Dataform, Google's product with essentially the same core idea as dbt — SQL models, a ref()-style dependency graph, tests — and now integrated into BigQuery specifically. Teams already committed to BigQuery sometimes pick Dataform for that native integration; teams that are warehouse-agnostic or already using Snowflake/Redshift/Databricks default to dbt, partly because of its broader adoption and ecosystem. Neither is categorically "better" — the honest answer in an interview is that the choice usually comes down to warehouse fit and existing team familiarity, not a decisive feature gap.

// Common Mistakes

Common Mistakes Beginners Make

Assuming dbt can pull data out of a production application database

A new user sometimes tries to point dbt directly at an operational Postgres database expecting it to "sync" data into the warehouse. dbt has no extraction capability at all — it only runs SQL against tables already present in the warehouse it is connected to. You need an ingestion tool (Fivetran, Airbyte, Stitch, a custom script) to land the raw data first. See Part 03.

Thinking a dbt model is a table dbt "owns" and manages state for

A dbt model is just a SELECT statement in a .sql file. Every time dbt runs, it re-executes that SELECT and rebuilds (or updates) the corresponding table or view in the warehouse. dbt does not maintain a separate copy of your data anywhere — the warehouse is the only place your data actually lives. Confusing this leads people to look for their data "inside dbt," which does not exist. See Part 03.

Writing all transformation logic in one enormous SQL query out of habit

Coming from a background of writing one huge nested-subquery SQL script, beginners often try to cram an entire pipeline into one dbt model instead of splitting it into small, layered models (staging → intermediate → mart) that reference each other with ref(). This throws away most of dbt's value — modularity, reusability, and a readable dependency graph. See Part 04 and Part 05.

Believing dbt Cloud and dbt Core are unrelated products with different transformation engines

They share the same underlying compilation and execution logic — dbt Cloud is dbt Core plus a hosted scheduler, browser IDE, and hosted documentation site. A model that works in dbt Core will produce the same compiled SQL in dbt Cloud. See Part 06.

Treating "dbt adoption" as purely a tooling decision with no process change

Teams sometimes install dbt and expect the "analytics engineering" benefits automatically, without adopting the surrounding practices — code review on model changes, actually writing tests, keeping documentation current. dbt makes those practices possible and convenient, it does not enforce them by itself. See Part 05.

// Error Library

Errors and Mix-Ups You'll Actually Hit

Module 01 is largely conceptual, so most of the "errors" a true beginner runs into are not stack traces from dbt itself yet — they're the mix-ups and false starts that come from applying the wrong mental model before you've run dbt for real. The list below mixes those early conceptual errors with the first genuine tool errors most learners hit in their first week.

error / symptom
`Compilation Error: Model 'stg_orders' depends on a node named 'orders' which was not found`

This happens when a ref('orders') call points at a model name that either has a typo, was renamed, or was never created. dbt resolves ref() calls at compile time by matching against the models it has actually discovered in your project — it does not guess. Check the exact filename (minus .sql) of the model you meant to reference.

error / symptom
`Database Error: Table "RAW_DB"."RAW_SCHEMA"."ORDERS" does not exist`

This is a source() problem, not a ref() problem — it means the raw table your source() call points to genuinely does not exist yet in the warehouse under that database/schema/table name. Usually the ingestion tool hasn't landed it yet, the name is misspelled in your sources YAML, or you're pointed at the wrong environment (dev vs. prod raw schema).

error / symptom
`Runtime Error: Parser Error: syntax error at or near "AS"` (or similar SQL syntax errors)

This means the SQL dbt generated after resolving your Jinja is not valid SQL for your warehouse. The fastest way to debug it is dbt compile, then open the corresponding file under target/compiled/ and read the actual generated SQL — it is often a stray comma, a mismatched Jinja {% if %}/{% endif %}, or a warehouse-specific SQL dialect difference.

error / symptom
`Warning: Nothing to do. Try checking your model configs and model specification args` (empty run)

dbt ran successfully but selected zero models — almost always because a --select flag or a model selector syntax typo excluded everything, or because the project has no models defined yet in the folder dbt is looking at.

error / symptom
`Encountered an error: Could not find profile named 'my_project'`

dbt could not find a matching profile in profiles.yml for the profile name declared in dbt_project.yml. This is a connection-configuration issue, not a modeling issue — check that the profile name in both files matches exactly and that profiles.yml exists in the expected directory (usually ~/.dbt/).

A sixth error, common in the first real run: a missing or misconfigured profile

error
Runtime Error
  Could not run dbt
  ProfileConfigError: The profile 'freshcart' in profiles.yml has invalid keys

A dbt project's dbt_project.yml declares which profile (a named connection configuration — warehouse type, account, credentials) it expects to use, and the actual connection details live in a separate profiles.yml file, deliberately kept outside the git-tracked project so credentials never end up committed to version control. This error means the profile name was found but something inside it doesn't match what dbt's adapter for that warehouse expects — usually a typo'd key name copied from an example for a different warehouse type (a BigQuery example field pasted into a Snowflake profile, for instance).

A seventh, very common early symptom: "I don't see my data anywhere"

A learner installs dbt, connects it to a warehouse, and runs their first model successfully — dbt reports success — but then can't find "their data" when they look around, because they were expecting to find it inside dbt somewhere rather than in the warehouse itself. This isn't a dbt error at all; it's the scope-boundary misconception from Part 03 showing up as a support question. The fix is simply to look in the actual warehouse (the schema dbt was configured to build into), where the table or view genuinely was created.

🎯 Key Takeaways

  • dbt is a command-line framework that turns version-controlled SQL SELECT statements ("models") into tables and views in your warehouse, and handles dependency ordering, testing, and documentation on top.
  • dbt is the "T" in ELT — the industry shifted from transforming data before loading it (ETL) to transforming it after loading it (ELT) once cloud warehouses made compute cheap and elastic, and dbt is purpose-built to run that transformation step inside the warehouse.
  • dbt does not extract data from source systems and does not load raw data into the warehouse — that is the job of ingestion tools like Fivetran and Airbyte. dbt only ever operates on data already sitting in the warehouse.
  • dbt Core is the free, open-source engine; dbt Cloud is dbt Labs' hosted product built around that same engine, adding a scheduler, browser IDE, and hosted docs — not a different transformation language.
  • The bundling of version control, automated testing, and generated documentation around SQL transformations is what gave rise to "analytics engineering" as its own discipline.
  • dbt is not the only option — hand-written SQL scripts and GUI ETL tools remain reasonable for some teams, and Dataform is a close, credible alternative, especially for BigQuery-native teams.
Share

Discussion

0

Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.

Continue with GitHub
Loading...