Why ref() Exists
The ref() function lets a model reference another dbt model by name instead of by a hardcoded, environment-specific schema and table name, and dbt resolves that reference at compile time into the correct fully-qualified path for whatever target you're running against — dev, staging, or prod. Because every ref() call is a dependency edge, dbt uses them to automatically build the DAG and determine the correct execution order, so a model referencing stg_orders will always run after stg_orders has been built.
Cricket analogy: This is like a captain calling for 'the death-overs specialist' rather than naming a specific player by name, so whichever bowler currently holds that role — even after a squad change — gets brought on automatically.
-- models/marts/fct_orders.sql
select
o.order_id,
o.customer_id,
c.email,
o.order_total
from {{ ref('stg_orders') }} as o
left join {{ ref('stg_customers') }} as c
on o.customer_id = c.customer_idsource() and Declaring Raw Data
The source() function points to raw, un-transformed tables that live outside of dbt's control — data loaded by a tool like Fivetran or an ELT pipeline — and it requires those tables to first be declared in a sources.yml file with a source name and table name. Declaring sources explicitly, rather than just writing raw table names into a FROM clause, lets dbt track raw data as a first-class node in the DAG, enabling source freshness checks and documentation alongside your models.
Cricket analogy: This is like officially registering a player's eligibility with the cricket board before they can be selected for the national team, rather than just having them show up and bat unofficially.
# models/staging/sources.yml
version: 2
sources:
- name: raw
database: analytics_raw
schema: app_production
tables:
- name: orders
loaded_at_field: _loaded_at
freshness:
warn_after: {count: 12, period: hour}
error_after: {count: 24, period: hour}
- name: customersRun dbt source freshness to check whether your declared sources are being loaded on schedule — it compares the current time against loaded_at_field and warns or errors based on the thresholds in sources.yml.
How ref() and source() Power the DAG
Every ref() and source() call dbt encounters while parsing your project becomes an edge in a directed acyclic graph, which is what powers commands like dbt run --select stg_orders+ (run this model and everything downstream of it) or dbt run --select +fct_orders (run this model and everything it depends on). Because the graph is built purely from these function calls rather than manual configuration, adding a new dependency is as simple as adding a ref() — dbt automatically recalculates the correct build order the next time it parses the project.
Cricket analogy: This is like how a bowling attack's rotation naturally emerges from who's fit and available that match, rather than being fixed months in advance — the team management just reads the current dependencies and slots bowlers in correctly.
Never bypass ref() or source() by writing a raw, fully-qualified table name directly into a model's SQL — doing so silently breaks the DAG, meaning dbt can no longer guarantee correct run order or detect that dependency during selection.
- ref() references another dbt model by name; dbt resolves it to the correct schema/table at compile time.
- source() references raw, un-transformed tables declared in a sources.yml file.
- Every ref()/source() call becomes an edge in dbt's dependency graph (the DAG).
- The DAG determines execution order and powers graph-based selectors like +model_name and model_name+.
- Declaring sources enables dbt source freshness checks and centralized documentation.
- Hardcoding raw table names instead of using ref()/source() silently breaks dependency tracking.
Practice what you learned
1. What is the main purpose of the ref() function in a dbt model?
2. Where must a raw table be declared before it can be referenced with source()?
3. What does every ref() or source() call contribute to in a dbt project?
4. What dbt command checks whether declared sources are loading on schedule?
Was this page helpful?
You May Also Like
SQL Models Basics
Learn how dbt models turn a plain SQL SELECT statement into a managed table or view, and how to organize them into a clean, layered project structure.
Materializations: View, Table, Incremental
Understand how dbt's view, table, and incremental materializations trade off build cost against query performance, and when to use each.
Jinja Templating in dbt
Learn how dbt uses the Jinja templating engine to add loops, conditionals, and variables to plain SQL, turning static queries into reusable, environment-aware code.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
ProgrammingPowerShell Study Notes
Programming · 30 topics