Data Engineering glossary
What is Medallion Architecture?
Medallion architecture is a convention for organising lakehouse tables into layers by quality: bronze holds raw data as received, silver holds cleaned, typed and deduplicated data, and gold holds business-ready models and aggregates. Each layer is rebuilt from the one before it, so downstream tables can always be recomputed.
How it works
The three layers each have a clear job:
| Layer | Holds | Typical work |
|---|---|---|
| Bronze | Raw data as received, append-only, with load metadata such as load time and source file | Land and keep; no business logic |
| Silver | Cleaned, typed, deduplicated and conformed records, often close to source entities | Parse, validate, deduplicate, apply CDC changes, join reference data |
| Gold | Business-ready data | Star schemas, aggregates, wide tables, feature tables |
Bronze exists so you can rebuild: if a parsing rule was wrong, fix it and recompute silver and gold from bronze without going back to the source. Silver is where quality rules live, and where bad records are quarantined rather than silently dropped. Gold is what dashboards, analysts and ML read.
Medallion is a layering convention, not a modelling method. It says nothing about how silver or gold are designed: silver might be normalised or a Data Vault, and gold is very often Kimball star schemas or one wide table per use case. The pattern was popularised by Databricks for lakehouses but maps directly onto the raw, staging and marts layers of a dbt project in a warehouse.
Some teams add layers (a quarantine area, a platinum or semantic layer). Keep the number small and the purpose of each one written down, or the layers turn into copies nobody can explain.
Example
Bronze keeps the raw strings, including a duplicate and a bad record. Silver types, validates and deduplicates. Gold aggregates for reporting.
CREATE TABLE bronze_orders (raw_id text, raw_amount text, loaded_at timestamptz DEFAULT now());
INSERT INTO bronze_orders (raw_id, raw_amount) VALUES
('1', '40.00'), ('2', '25.50'), ('2', '25.50'), ('3', 'n/a');
CREATE TABLE silver_orders AS
SELECT DISTINCT raw_id::int AS order_id, raw_amount::numeric AS amount
FROM bronze_orders
WHERE raw_amount ~ '^[0-9]+(\.[0-9]+)?$';
CREATE TABLE gold_revenue AS
SELECT count(*) AS orders, sum(amount) AS revenue FROM silver_orders;
SELECT (SELECT count(*) FROM bronze_orders) AS bronze_rows,
(SELECT count(*) FROM silver_orders) AS silver_rows,
g.revenue
FROM gold_revenue g;
bronze_rows | silver_rows | revenue
-------------+-------------+---------
4 | 2 | 65.50
In a real pipeline, the record with amount n/a would also be written to a quarantine table so someone can see why it was rejected.
Common mistakes
- Putting business logic in bronze, which removes the ability to rebuild from what the source actually sent.
- Dropping bad records between bronze and silver without counting or quarantining them.
- Treating medallion as a data model and skipping the design of gold tables.
- Letting dashboards read silver directly, so every report reimplements the same business rules.

