Skip to content
Everyone knows 3 min read · All formats 3 practice problems ↓

The medallion architecture

Bronze, silver and gold layers: what belongs in each, and where the pattern goes wrong.

You will learn

  • What the bronze, silver and gold layers hold
  • Which transformations belong in each layer
  • How data flows and is reprocessed between layers
  • Where the pattern goes wrong

Read first

Comfortable with these? Read on.

TL;DR The medallion architecture organises lakehouse tables in three layers of increasing quality: bronze (raw data as received), silver (cleaned, deduplicated, conformed) and gold (business-level aggregates and models ready for consumption).

The three layers

LayerHoldsTypical operationsConsumers
BronzeRaw data, as close to the source as possible, plus ingestion metadataAppend-only loads, minimal parsing, add load timestamp and source fileData engineers, reprocessing
SilverValidated, deduplicated, typed and joined data at the entity levelType casting, deduplication, MERGE of CDC, conforming keys, quality checksEngineers, analysts, data scientists
GoldBusiness-ready tables: facts and dimensions, aggregates, featuresAggregations, business logic, star schemasBI dashboards, reports, ML, applications

Walk through one pipeline

Bronze · land raw

JSON order events from Kafka are appended to bronze.orders_raw exactly as received, with _ingested_at and _source columns. Malformed records are kept, not dropped: bronze is your replayable history.

The command

raw = spark.readStream.format("kafka").option("subscribe", "orders").load()
(raw.selectExpr("CAST(value AS STRING) AS json", "timestamp AS _ingested_at")
    .writeStream.option("checkpointLocation", ckpt).toTable("bronze.orders_raw"))

Silver · clean and merge

Parse JSON into typed columns, drop or quarantine invalid rows, deduplicate by order_id keeping the latest event, and MERGE into silver.orders so it holds one current row per order.

The command

MERGE INTO silver.orders t
USING latest_order_events s
ON t.order_id = s.order_id
WHEN MATCHED AND s.event_ts > t.event_ts THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *

Gold · serve

Build gold.daily_revenue by date and region, and gold.fct_orders joined to customer and product dimensions. These are the tables dashboards query.

The command

CREATE OR REPLACE TABLE gold.daily_revenue AS
SELECT order_date, region, SUM(amount) AS revenue, COUNT(*) AS orders
FROM silver.orders JOIN silver.customers USING (customer_id)
GROUP BY order_date, region

Why layers help

  • Reprocessing: when silver logic has a bug, rebuild silver from bronze instead of re-extracting from source systems.
  • Clear contracts: consumers know gold is curated and stable; bronze carries no guarantees.
  • Separation of concerns: ingestion, cleaning and business logic change at different speeds and are owned by different people.
  • Debugging: you can compare each layer to find where a number went wrong.

Where it goes wrong

Layers as a rule, not a tool

Not every dataset needs three copies. A clean source table can go straight to silver.

Business logic in bronze

Transforming on ingestion destroys the raw record you need for replays.

Gold tables that copy silver

Gold should add value: aggregation, modelling, business rules.

No retention on bronze

Raw data grows forever; set retention, but keep enough history to rebuild silver.
Note: the names are a convention popularised by Databricks; the idea of raw, cleaned and curated zones is much older. Some teams use staging, core and marts, which map to the same three ideas.

Common mistakes

Dropping malformed records at ingestion

Keep them in bronze or a quarantine table; you may need them.

Letting dashboards read silver directly

Consumers then depend on technical tables that change.

Ignoring idempotency

Each layer should be safely re-runnable: MERGE or overwrite by partition, not blind appends.

Key takeaways

  • Bronze keeps raw data, silver cleans and conforms it, gold serves the business.
  • Bronze is the replayable history; do not transform it destructively.
  • Silver is where deduplication and MERGE of changes happen.
  • Use layers where they add value, and make each step idempotent.

Check yourself

3 questions

1. Where should malformed source records go?

Show the answer

Kept in bronze or a quarantine table. Bronze preserves raw data so problems can be investigated and replayed.

2. Which layer typically holds deduplicated entity tables updated by MERGE?

Show the answer

Silver. Silver is the cleaned, conformed, entity-level layer.

3. Main benefit of keeping bronze?

Show the answer

Rebuilding downstream tables without re-extracting from sources. Raw history lets you rerun silver and gold logic after fixes.

Practice it

Interview problems that use this: write the PySpark, run it, and get graded on hidden tests.

Solve: Apply a CDC Batch (MERGE Semantics) →

Go deeper

Primary sources: Delta Lake: best practices