Skip to content
Reliable Data Engineering
Lesson
Open in the interactive app

Lakehouse and Medallion Architecture

Lakehouse and medallion architecture


1. How we got here: warehouse → lake → lakehouse

Data warehouse (Teradata, Redshift, Snowflake)Data lake (HDFS/S3 + Hive)Lakehouse (Delta/Iceberg/Hudi on object storage)
StorageProprietary, coupled to compute (classic)Open files, cheapOpen files + table format, cheap
Data typesStructuredAnythingAnything
ACID / updates✅❌ (rewrite whole partitions)✅
SchemaOn write, enforcedOn read, often chaos (“data swamp”)Enforced + evolvable
BI performanceExcellentPoorGood–excellent (caching, clustering, Photon/vectorised engines)
ML / Python accessExport neededDirectDirect
Cost$$$$$–$$
Lock-inHighLowLow (open formats)

One-line definition for interviews:

“A lakehouse puts warehouse capabilities (ACID transactions, schema enforcement, governance, fast SQL) on top of open file formats in cheap object storage. BI, data science and streaming then share one copy of the data instead of copying it between a lake and a warehouse.”

What makes it possible: an open table format writes a transaction log (Delta _delta_log/*.json, Iceberg metadata/manifest tree) that records which Parquet files make up each table version. Readers see a consistent snapshot; writers commit atomically with optimistic concurrency. See Storage & table formats.


2. Medallion architecture

A convention for layering data by quality and purpose, not a technology. Bronze → Silver → Gold.

flowchart LR
    SRC[Sources] -->|"as-is, append"| B[("Bronze<br/>raw")]
    B -->|"clean, dedupe, conform,<br/>MERGE / SCD2"| S[("Silver<br/>enterprise entities")]
    S -->|"model, aggregate,<br/>business logic"| G[("Gold<br/>marts, KPIs, features")]
    G --> BI[BI]
    G --> ML[ML]
    G --> APP[Apps / APIs]
    S -.->|"data scientists<br/>explore here"| DS[Notebooks]

Layer contracts (what each layer promises)

BronzeSilverGold
PurposeLand everything, lose nothingOne clean, conformed version of each entityAnswer business questions fast
ShapeSource-shaped (mirrors source tables/events)Entity-shaped (customer, order, device)Consumption-shaped (star schema, OBT, aggregates)
WritesAppend-onlyMERGE / upsert / SCD2Overwrite / incremental MERGE
SchemaLoose (_rescued_data, VARIANT/JSON)Enforced, typedEnforced, documented, versioned
QualityNone, but metadata addedExpectations, dedup, quarantineBusiness rules, reconciled totals
RetentionLong (it’s your replay source)LongAs needed
OwnersPlatform / ingestion teamDomain data engineersDomain + analytics engineers
ConsumersData engineers onlyDE, DS, advanced analystsEveryone, BI tools, apps
PIIRaw (restricted access)Tagged, masked/tokenisedMinimised / aggregated

Metadata columns to add in bronze

SELECT
  *,
  current_timestamp()          AS _ingested_at,
  _metadata.file_path          AS _source_file,      -- AutoLoader / file sources
  'crm_postgres.orders'        AS _source_system,
  :pipeline_run_id             AS _run_id
FROM ...

For Kafka sources also keep topic, partition, offset, timestamp: they’re your dedup and replay keys.


3. Design decisions interviewers ask about

”Why not go straight from source to gold?”

”Isn’t three copies of the data wasteful?”

Storage is ~$23/TB-month; compute and engineer time cost far more. Mitigate with retention policies (bronze → cold tier after N days), VACUUM, and views instead of tables for thin gold layers.

”How many layers?”

Medallion is a starting point. Real platforms often have more, e.g. a six-layer enterprise layout:

flowchart LR
    L0["0 · Landing<br/>files as delivered"] --> L1["1 · Bronze<br/>raw Delta"]
    L1 --> L2["2 · Silver<br/>conformed entities"]
    L2 --> L3["3 · GDPR / privacy layer<br/>pseudonymised, consent-filtered"]
    L3 --> L4["4 · Gold<br/>domain marts"]
    L4 --> L5["5 · Secure / ABAC serving layer<br/>row filters, column masks per persona"]

Each extra layer must earn its place with a distinct contract (e.g. “nothing downstream of layer 3 can contain direct identifiers”). Adding layers just to look organised is an anti-pattern.

”Batch or streaming between layers?”

Both work. Delta tables are both a batch table and a streaming source/sink, so bronze → silver can be readStream with trigger(availableNow=True) (incremental batch) today and a continuous stream tomorrow, with the same code.

(spark.readStream.table("bronze.orders")
   .withWatermark("event_ts", "1 hour")
   .dropDuplicatesWithinWatermark(["order_id"])
   .writeStream
   .foreachBatch(merge_into_silver)            # idempotent MERGE
   .option("checkpointLocation", "/chk/silver_orders")
   .trigger(availableNow=True)                 # or processingTime="1 minute"
   .start())

4. Anti-patterns

Anti-patternWhy it hurtsFix
Business logic in bronzeCan’t replay “raw” anymoreBronze = as-landed + metadata only
Gold tables built from bronze directlyEach gold re-implements cleaning differentlyGold reads silver only
Silver mirrors source tables 1:1 foreverNo conformance, every consumer joins 12 tablesModel entities in silver
”Gold” = 400 one-off tables per dashboardMetric drift, costConformed marts + semantic layer
No ownership per layerNobody fixes breakagesOwners + SLAs per table
Partitioning by high-cardinality columnMillions of small filesPartition by date (or not at all) + clustering
Every layer a full overwriteCost explodes with data growthIncremental processing (CDF, streaming, MERGE)

5. Lakehouse platform components (vendor-neutral map)

CapabilityDatabricksOpen / AWSSnowflake-centric
Table formatDelta (UniForm → Iceberg readers)Iceberg / HudiIceberg tables / native
Catalog & governanceUnity CatalogGlue, Lake Formation, Polaris, NessieHorizon / Polaris
Batch engineSpark / PhotonSpark (EMR), Trino, AthenaSnowflake
StreamingStructured Streaming, DLT / LakeflowFlink, KinesisSnowpipe Streaming, Dynamic Tables
Transformationsdbt, DLT, notebooksdbt, Sparkdbt, Dynamic Tables
OrchestrationWorkflows / Lakeflow JobsAirflow (MWAA), Step FunctionsTasks, Airflow
BI SQLDatabricks SQL warehousesAthena, Trino, Redshift SpectrumWarehouses
MLMLflow, Feature Store, Model ServingSageMakerSnowpark ML

6. Interview questions

Explain the medallion architecture to a non-technical stakeholder.

“Like a water treatment plant. Bronze is water straight from the river: we keep all of it in case we need to re-treat it. Silver is filtered and cleaned. Gold is bottled for a specific use: drinking, cooking, industry. If we change the filtering process, we re-filter from the river water we saved.”

Where would you apply data quality checks and what happens on failure?

Bronze: structural only (parsable, required metadata), and bad records go to a rescue column or quarantine; never drop silently. Silver: schema, types, uniqueness of business keys, referential checks; violations go to a quarantine table with reason codes, with alerting on thresholds. Gold: business rules and reconciliation (totals match finance/source), and failure blocks publishing (write-audit-publish) so consumers keep seeing the last good version.

How do you handle a source that sends full snapshots daily instead of changes?

Land each snapshot in bronze partitioned by snapshot date. Derive changes by comparing to the previous snapshot (hash of non-key columns → inserts/updates/deletes), then MERGE into silver as SCD1/SCD2. Keep a limited number of snapshots in bronze (cost) once silver history is trustworthy.

Lakehouse vs cloud warehouse: which would you choose for a new company?

It depends on workloads and team: mostly SQL/BI with a small team → a managed warehouse is fastest to value. Heavy ML, streaming, semi-structured data, very large volumes, or a strong desire to avoid lock-in → a lakehouse. Increasingly the line is blurring (warehouses read Iceberg, lakehouses have serverless SQL). The deciding factors are the open format and one copy of data for all engines.