ArticleBest Practices

Stop Calling Them Bronze, Silver, and Gold

The medallion architecture gets the structure right but the naming wrong. Six purpose-driven layers (Source Mirror, Data Prep, Enrich, Integrate, Core, Serve) replace vague color metaphors with names that document themselves.

February 17, 20268 min read
#dbt#data modeling#analytics engineering#medallion architecture#best practices

Key Information

Type

Article

Category

Best Practices

Reading Time

8 min read

Last Updated

February 17, 2026

"What does the silver layer do?"

"It's the intermediate layer."

"Intermediate between what?"

"Between bronze and gold."

"So what makes something bronze vs. silver?"

"...it depends."

Sound familiar?

The medallion architecture gives you a ranking system when what you actually need is a purpose system. The names tell you order, not intent. When names don't carry meaning, people fill in the gaps, and you end up with five different interpretations of "silver layer" across five teams.

What Medallion Gets Right

The core idea is sound. Separating data by what's been done to it is one of the most durable patterns in data engineering. Raw data isolated from transformed data. Business logic separated from source-specific cleanup. Consumer-facing models curated and governed.

Medallion gets the structure right. The naming fails.

dbt's default directory names (staging, intermediate, marts) were a fine starting point. But defaults aren't sacred. Your project should use names that describe what each layer does.

Where the Metaphor Breaks

New hires read folder names, not wikis.

Your onboarding docs might have a beautiful diagram explaining what each medallion layer means. Nobody reads it. People navigate by folder name, file name, and schema name. If those names are "bronze," "silver," and "gold," the new hire learns exactly nothing about what each layer is for. The information that should be embedded in the system lives in a Confluence page that's already three versions behind.

Three layers aren't enough.

Staging alone handles at least two distinct jobs: mirroring source data exactly as it arrives, and prepping that data (renaming fields, casting types) for downstream use. Different responsibilities. Different rules. Stuffing them both into "bronze" hides that distinction.

"Silver" has no defined purpose.

Ask ten data engineers what belongs in the silver layer. You'll get ten answers. Is it where you join across sources? Apply business logic? Both? The name doesn't tell you, so every team invents their own definition, then struggles to enforce it because the boundary was never clear.

We've seen teams burn sprint cycles debugging models that landed in the wrong layer because nobody agreed on what "silver" meant. That's not a documentation problem. That's a naming problem.

Purpose-Driven Layer Names

Replace the ranking metaphor with names that describe what each layer does. Six layers, each with a single responsibility:

LayerDirectoryPurpose
Source Mirror`source_mirror/`1:1 source copy; changelog, track deletes
Data Prep`data_prep/`Field renaming, type casting (no business logic)
Enrich`enrich/`Source-specific business logic
Integrate`integrate/`Combining data from different systems
Core`core/`Org-wide canonical analytical tables
Serve`serve/`Consumer-specific, pre-shaped delivery

Each name is a verb that tells you exactly what happens at that stage. You mirror source data, prep it, enrich it with business logic, integrate across sources, define core canonical models, and serve them to consumers.

"What does the Enrich layer do?" It enriches cleaned source data with business logic: calculating LTV from Stripe events, classifying transactions from your billing system. "What's the difference between Enrich and Integrate?" Scope: Enrich works within a single source, Integrate works across sources.

No wiki required.

Where Do Schemas Fit?

If you're on Snowflake, these layer names map directly to your schema configuration in dbt_project.yml. Each layer becomes its own schema, which means your Snowflake object explorer mirrors your dbt project structure:

dbt_project.yml
# dbt_project.yml
models:
  your_project:
    source_mirror:
      +schema: source_mirror
      +materialized: view
    data_prep:
      +schema: data_prep
      +materialized: view
    enrich:
      +schema: enrich
      +materialized: view
    integrate:
      +schema: integrate
      +materialized: view
    core:
      +schema: core
      +materialized: table
    serve:
      +schema: serve
      +materialized: table

Now when someone browses ANALYTICS.CORE in Snowflake, the schema name tells them exactly what they're looking at: canonical, org-wide analytical tables. No guessing whether ANALYTICS.SILVER contains cleaned source data or fully modeled business entities.

What About the Semantic Layer?

If you're using a semantic layer (MetricFlow, Looker LookML, Cube), it sits on top of Core and Serve; it doesn't replace them. The semantic layer defines metrics and dimensions; the warehouse layers define the tables those metrics query against.

Think of it this way: Core is where your canonical fct_orders and dim_customers live. The semantic layer is where you define that "revenue" means sum(order_total) where status = 'completed'. Different concerns, complementary roles. Purpose-driven layer names make that boundary clearer, not muddier.

Rename Your Directories

The change is mechanical. Rename directories, move models, and the folder name does the explaining from that point forward.

Before: Default dbt directories

models/
├── staging/
│   ├── base/
│   └── ...
├── intermediate/
└── marts/

After: Purpose-driven directories

models/
├── source_mirror/
├── data_prep/
├── enrich/
├── integrate/
├── core/
└── serve/

A model enriching Stripe data with business logic lives in enrich/stripe/. A model stitching Stripe customers to Salesforce accounts lives in integrate/. A model shaped for the finance team's dashboard lives in serve/. The name does the explaining.

The Fast Lane

Six layers is the right default. Not every data flow should traverse all six.

Real-time inventory feeds, fraud-detection signals, operational alerts. Some data needs to move faster than a full pipeline allows. For these cases, build a fast lane: a model that goes directly from Source Mirror to Serve, skipping the layers in between.

The rules:

  • Tag the model explicitly so it's visible in your pipeline
  • Document why it bypasses the standard path
  • Revisit quarterly. Fast lanes tend to become permanent shortcuts
  • Never let a fast lane model feed back into the standard layers
-- models/serve/fast_lane/retail_usa_realtime_inventory.sql
{{
    config(
        materialized='incremental',
        schema='retail_usa_data',
        tags=['fast_lane'],
        unique_key='inventory_event_id'
    )
}}

-- Bypasses Enrich/Integrate layers for latency-sensitive inventory data
-- Reviewed: 2025-Q1 | Owner: @retail-data-team
select
    event_id as inventory_event_id
    , store_id
    , sku
    , quantity_on_hand
    , updated_at
from {{ ref('base_inventory__stock_events') }}
{% if is_incremental() %}
where updated_at > (select max(updated_at) from {{ this }})
{% endif %}

The fast lane is a conscious trade-off, not a shortcut. Label it and people treat it accordingly.

Migration Path

You don't need to rename everything in a weekend.

1. New models get purpose-driven names from day one.

No exceptions. Zero-cost entry point.

Action: Update your PR template to require a purpose-driven directory for every new model. Five minutes.

2. Rename directories incrementally.

One source system at a time, one layer at a time.

# Split staging into the two layers it actually contains
mv models/staging/base models/source_mirror
mv models/staging models/data_prep
mv models/intermediate models/enrich   # or split into enrich/ and integrate/
mv models/marts models/core            # or split into core/ and serve/

3. Update dbt_project.yml.

dbt_project.yml
# dbt_project.yml
models:
  your_project:
    source_mirror:
      +materialized: view
    data_prep:
      +materialized: view
    enrich:
      +materialized: view
    integrate:
      +materialized: view
    core:
      +materialized: table
    serve:
      +materialized: table

4. Communicate the change.

Update your project README, add a one-page reference in your docs folder, mention it in standup. The explanation is one sentence: "We renamed our warehouse schemas so the names describe what each layer does."

Get Started

Your layer names should describe what data does at each stage, not where it ranks in a hierarchy.

The migration is incremental. Start with new models this week, rename directories next sprint, and within a month your project explains itself. Less time in onboarding calls explaining what "silver" means. More time in PR reviews that catch logic errors instead of "wait, should this be in silver or gold?"

We've seen this naming pattern pay off consistently across client projects: descriptive layers cut onboarding confusion, reduce misplaced models, and speed up code review. If your team is outgrowing its current conventions or building from scratch, we'd be happy to take a look. Book a free 30-minute architecture review →