Learn 05~7 min
The DAG
source() and ref() do more than replace hardcoded table names: they turn the project into a dependency graph that dbt can build, test and explain.
Never hardcode a table
In a Snowflake worksheet you would write from STAGING.CSDS.STG_CSDS_BRIDGING. In dbt you write:
from {{ ref('stg_csds_bridging') }}And not only in from — ref() goes anywhere a table name would: joins, CTEs, subqueries. A model reading two upstream models looks like this:
select
wl.sk_patient_id,
wl.week_ending_date,
dict.specialty_name
from {{ ref('stg_wl_wl_openpathways_data') }} wl
left join {{ ref('stg_dictionary_dbo_specialties') }} dict
on wl.treatment_function_code = dict.bk_specialty_codeEach call does two things:
- dbt resolves the location for you. Developing, it points at the DEV__ databases; in production, the production ones. Same SQL, every environment.
- dbt records the dependency. Your model now officially sits downstream of
stg_csds_bridging— it appears in lineage, builds in the right order, and anyone changing that staging model can see you depend on it.
How the resolution works
The mechanism has three steps. When dbt starts, it parses every file in the project, extracting the ref() and source() calls without running any SQL — that alone is enough to assemble the whole dependency graph. It then compiles each model, replacing every call with a fully qualified table name. Only then does it run anything, walking the graph in dependency order.
The name a call compiles to depends on the target. A target is a named set of connection settings in the project's profiles.yml: which Snowflake account and role dbt connects with, and — the part that matters here — which databases it reads from and builds into. Environment setup gave you a development target that points at the DEV__ databases; the scheduled workflows run the same project with a prod target that points at the production ones. The same line compiles differently depending on which target runs it:
-- in the model
from {{ ref('stg_csds_bridging') }}
-- compiled with the development target
from DEV__STAGING.CSDS.STG_CSDS_BRIDGING
-- compiled with the prod target (deploys and scheduled builds)
from STAGING.CSDS.STG_CSDS_BRIDGINGYour SQL never mentions an environment; the target supplies the location at compile time, using the project's naming rules — database from the layer, schema from the domain. This is why development is safe by construction: the same model text writes to DEV__ databases on your machine and to production in the workflows, and dbt compile shows you exactly what either would run. The production workflow lesson builds on the same mechanism — CI validation compiles your changed models with the development target while resolving unchanged parents to their production relations.
source() — the entry point
Tables we do not build — the feeds landing in the data lake databases (DATA_LAKE, plus DATA_LAKE__NCL) — are declared once in YAML under models/sources/, then referenced with source():
from {{ source('csds', 'ActiveSubmission') }}In this project, only generated raw models call source(). Everything you write uses ref(). That keeps a single, stable interface to the outside world: if a feed changes, only the raw layer moves. Source declarations themselves are produced by a mapping pipeline — covered in the Find your source field guide — so you rarely write them by hand either. The official dbt documentation explains the general behaviour of ref() and source(); this project's raw-layer restriction is a local convention on top of it.
The DAG
From every ref() and source() call, dbt assembles the whole project into a directed acyclic graph. This is a real slice of ours:
The DAG is what makes dbt build -s +my_model possible: the + means “and everything upstream”, and dbt knows exactly what that is. It is also why circular references are impossible — dbt refuses to compile them.
The same graph is a discovery tool. The finding models lesson shows how to inspect ancestors and descendants before deciding that a new model is needed.
Quiz
0/4 answered1Why is hardcoding STAGING.CSDS.STG_X a problem?
2Where are you allowed to use source()?
3ref('stg_csds_bridging') compiles to DEV__STAGING.CSDS.STG_CSDS_BRIDGING on your machine and STAGING.CSDS.STG_CSDS_BRIDGING in production. What decides?
4What does the + in dbt build -s +int_wl_current select?