---
title: "dbt models mixing ref() and source() | Metacenta"
description: "A dbt model that reads both a raw source and a staged model mixes cleaned and uncleaned columns in one query. Why that hides mismatches, and the fix."
url: "https://metacenta.com/rules/direct-join-to-source"
updated: "2026-10-14"
tokens: 641
---

Modelling correctness · rule direct-join-to-source

# dbt models mixing ref() and source()

A Metacenta review checks this under the rule **A model does not mix refs and raw sources**. Everything below applies whether or not you ever commission one.

## What this rule checks

This rule flags any model that depends on both a `source()` and a `ref()` to another model. It reads the model's declared dependencies, not its SQL, so it applies whether or not the two are joined. It does not need a layered project.

## Why it matters

One side has been through staging's renaming and casting, and the other has not. That is where a silent type or column-name mismatch survives review.

## How to fix it

**Stage raw sources before joining them to models.** Stage the source first, then read the resulting model alongside the other `ref()`.

Before:

```sql
select o.order_id, p.amount
from {{ ref('stg_shop__orders') }} as o
join {{ source('stripe', 'payments') }} as p
  on p.orderid = o.order_id
```

After:

```sql
select o.order_id, p.amount
from {{ ref('stg_shop__orders') }} as o
join {{ ref('stg_stripe__payments') }} as p
  on p.order_id = o.order_id
```

## When it is fine to leave

A small, static source such as a currency list, already clean at load, can be read directly beside a model. Describe the reason on the model. A staging model over it costs little, though, and ends the question.

## What we need to check it

`manifest.json` alone. We read each model's `depends_on`, so the rule works in flat and layered projects alike.

## Published rules it corresponds to

**[dbt_project_evaluator](https://dbt-labs.github.io/dbt-project-evaluator/)**, rule `fct_direct_join_to_source`. Ours checks the same condition and threshold.

This means our check corresponds to their rule. It does not mean the publisher reviewed or endorses it.

[Ask about a review](https://metacenta.com/enquire?src=rule-direct-join-to-source&via=md)

## Related pages

- [dbt incremental models: how unique_key works](https://metacenta.com/rules/incremental-without-unique-key)
- [dbt public models with no enforced contract](https://metacenta.com/rules/public-model-without-contract)
- [dbt sources declared twice for one table](https://metacenta.com/rules/duplicate-source)
- [dbt staging models: how to set up the staging layer](https://metacenta.com/rules/staging-depends-on-staging)
- [A sample review, as a client reads it](https://metacenta.com/sample)
- [All published rules](https://metacenta.com/rules)
- [How a review is assessed](https://metacenta.com/standard)
