Where dbt sits
In Level 1 we walked through the nightly orders pipeline we're going to migrate: data comes out of the order system and the CRM, gets reshaped by a graphical ETL package and some stored procedures, runs on a timer, and finishes by pushing results back out. Before we write any dbt, you need to know which parts of that pipeline dbt can take over and which parts it was never meant to touch. Teams that get this wrong end up writing Python loaders inside dbt macros, or they assume their scheduler will go away when it won't.
The short version: dbt is the T in ELT. dbt's own documentation says it does not extract or load data. Its job is to transform data that is already in your warehouse. You write SELECT statements, and dbt wraps them in the DDL/DML needed to turn them into tables and views, in dependency order.
A kitchen analogy helps. dbt is the recipe book and the chef. It does not do the grocery shopping (ingestion), it doesn't set the restaurant's opening hours (orchestration), and it doesn't deliver the food (reverse ETL and delivery).
What "compiles SELECTs into warehouse objects" means
In the legacy estate, a stored procedure handles both the logic and the plumbing:
-- Legacy: the procedure owns logic AND object management
CREATE PROCEDURE dbo.usp_build_daily_revenue AS
BEGIN
TRUNCATE TABLE rpt.daily_revenue; -- plumbing
INSERT INTO rpt.daily_revenue (order_date, revenue) -- plumbing
SELECT CAST(o.order_ts AS date), SUM(o.amount) -- the actual logic
FROM stg.orders o
WHERE o.status <> 'CANCELLED'
GROUP BY CAST(o.order_ts AS date);
END
In dbt you keep only the logic. You'll build one of these properly in Level 5; for now, just notice what's missing:
-- dbt: a file containing ONLY a SELECT
select cast(order_ts as date) as order_date,
sum(amount) as revenue
from raw_orders -- already loaded by something else!
where status <> 'CANCELLED'
group by 1
dbt generates the create table ... as or create view ... wrapper, works out the build order from the dependencies between models, and runs everything against the warehouse. The compute happens in the warehouse, not in dbt.
Sorting the legacy pipeline
Now we'll take each step of the pipeline and put it in one of four buckets: ingestion (getting data in), transformation (reshaping it), orchestration (when things run and in what order), and delivery (getting results out to people and systems).
| # | Legacy step | Bucket | dbt takes it over? |
|---|---|---|---|
| 1 | ETL package pulls orders from the OLTP database | Ingestion | No |
| 2 | CRM CSV file collected from SFTP | Ingestion | No |
| 3 | In-flight lookups, trims and currency conversion inside the ETL package | Transformation | Yes, after the raw data lands |
| 4 | Stored procs build customer dimension and orders fact | Transformation | Yes, this is dbt's core job |
| 5 | Row-count check proc | Transformation / quality | Mostly: it becomes dbt tests |
| 6 | 02:00 scheduled job with step dependencies | Orchestration | Partly (see below) |
| 7 | Failure email to the on-call DBA | Alerting | Partly (platform only) |
| 8 | Customer segments pushed back into the CRM | Delivery (reverse ETL) | No |
Gotcha: in-flight transforms. Step 3 runs between extract and load, so the warehouse never sees the raw values. dbt can only transform what's already in the warehouse, so the migration changes the order: land the raw rows untouched first, then re-express those lookups as SQL models. As a bonus you get an auditable raw copy you never had before. The costs: more storage, and somebody has to decide how long raw data is kept.
The neighbours dbt needs
Loading (ingestion). You need a tool that copies source data into the warehouse. That could be a managed connector service (dbt's docs point to tools such as Fivetran and Stitch), a warehouse-native loader, or your own scripts. dbt does have dbt seed for loading CSVs, but the docs say plainly that seeds should not be used for raw data such as production exports, because they're slow on large files. Use seeds for small, version-controlled lookup lists like country codes.
Scheduling (orchestration). Something has to say "run at 02:00, but only after the load has finished". dbt Core is a command-line tool with no scheduler, so it needs cron, Airflow or a similar tool. dbt's commercial platform does have a job scheduler that runs jobs on a cron schedule or when another job finishes, but it only schedules dbt. If your extract runs somewhere else, you still need to coordinate the two, either through an external orchestrator or an API trigger.
# dbt Core under an external orchestrator: the orchestrator decides WHEN
# step 1: loader finishes (not dbt)
# step 2: then
dbt build # runs models and tests in dependency order
# step 3: then trigger reverse ETL sync (not dbt)
Pushing data back (reverse ETL). Step 8 sends warehouse data into an operational app. dbt can build the customer_segments table that feeds it, but it won't call the CRM's API. You'll need a reverse ETL tool or a custom sync job.
Alerting. dbt tests catch bad data, and failed tests show up as failed runs. Who gets told, and how, depends on your setup. The commercial platform can send job notifications by email, Slack or Microsoft Teams when a job fails or warns. With Core, you wire notifications up through your orchestrator.
Trade-off: one vendor or several? Fivetran and dbt Labs completed their merger on 1 June 2026, and Fivetran already sells reverse ETL (formerly Census, now Fivetran Activations). One company now sells several of these buckets, but they are still separate products with separate jobs. Bucket your pipeline by job, not by vendor. Level 3 covers the landscape.
Key takeaways
- dbt is a transformation framework. You write SELECTs, and dbt turns them into tables and views in the warehouse, in dependency order.
- dbt assumes ELT: raw data must already be loaded. Logic that runs in flight has to move after the load.
- Ingestion, reverse ETL and delivery stay with other tools. Orchestration and alerting stay outside dbt Core, and the commercial platform covers only some of them.
dbt seedis for small reference CSVs, not for loading raw data.- Sort each legacy step by what it does before you decide what to migrate.