For years, data teams stitched together pipelines that copied the same datasets into two or three systems so analysts could run SQL while data scientists read raw Parquet. It was wasteful, and the data lakehouse exists because the industry decided that maintaining two separate storage paradigms was an expensive mistake. This guide walks through each layer of a production lakehouse, compares the three dominant implementation paths, and lays out a cost-modeling framework for 2026.
What a lakehouse is, and isn't
A data lakehouse is a single storage architecture combining the cheap, scalable, schema-on-read flexibility of a data lake with the ACID transactions, schema enforcement, and performance of a warehouse. The key enabler is open table formats - Apache Iceberg, Delta Lake, Apache Hudi - which turn commodity object storage into something that behaves like a warehouse without a proprietary engine. It is not "a data lake with a BI tool on top" (without ACID and metadata management you still have a swamp); not a replacement for operational databases (OLTP still belongs in PostgreSQL or MySQL); not automatically cheaper than a warehouse (it depends on query patterns and storage-lifecycle management); and not a single product, but an architectural pattern you can assemble from open-source components or buy as a managed service. By 2026 the lakehouse-vs-warehouse debate has largely settled - Snowflake added Iceberg Tables, Databricks built Unity Catalog, Google pushed BigLake. The lakehouse subsumes the warehouse rather than competing with it.
The five layers
A production lakehouse is a stack of five layers, bottom to top:
- Object storage. All data lives as Parquet/Avro/ORC files on commodity object storage (S3, GCS, ADLS, MinIO). This is the cost advantage - S3 Standard runs ~$0.023/GB/month versus ~$0.040 for compressed warehouse storage, and the gap widens at petabyte scale.
- Open table format. The layer that makes a lakehouse a lakehouse: ACID transactions, schema evolution, time travel, partition evolution (Iceberg), and file-level statistics for predicate pushdown. As of 2026, Iceberg is the de facto standard - natively supported by Snowflake, Databricks, AWS, Google, and Cloudera. Start a new lakehouse on Iceberg unless you're deeply invested in Databricks, where Delta with UniForm (which exposes Delta as Iceberg) is the pragmatic choice.
- Catalog and governance. The brains: it maps logical table names to physical files, tracks schema versions, manages access policies, and enables discovery. Look for namespace hierarchy, fine-grained (column/row) access control, lineage, multi-engine support, and Git-like branching (Nessie). Without a real catalog, your lakehouse is just a tidy lake.
- Compute engine. Storage-compute separation lets multiple engines read the same data without copying - Spark for heavy transforms, Trino or Starburst for interactive SQL, Flink for streaming upserts, DuckDB for local dev - all reading the same Iceberg tables with the same ACID guarantees.
- Consumption. BI tools, notebooks, ML frameworks, and APIs connect through the compute layer. Because everyone reads the same governed tables, the "which number is right?" problem of the two-tier era disappears.
Medallion architecture: bronze, silver, gold
Bronze holds raw data exactly as it arrived, with metadata columns appended - no transforms, no dedup - as an immutable audit trail:
bronze_events = (raw_events
.withColumn("_ingested_at", current_timestamp())
.withColumn("_source_file", input_file_name())
.withColumn("_batch_id", lit("batch-2026-02-20-001")))
bronze_events.writeTo("lakehouse.bronze.events") \
.option("write.parquet.compression-codec", "zstd").append()
Zstandard compression at bronze gives 20-30% better ratios than Snappy at comparable decompression speed - meaningful at raw volumes. Silver is where data is deduplicated, validated, typed, and conformed; schema enforcement is aggressive, and non-conforming records go to quarantine, not into silver. Idempotent upserts via MERGE:
MERGE INTO lakehouse.silver.events AS target
USING incoming AS source
ON target.event_id = source.event_id
WHEN MATCHED AND source.event_ts > target.event_ts THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *
Gold tables are purpose-built for specific consumption - aggregated, denormalized, partitioned by the common filter (usually date) and sorted within partitions by the common join key:
CREATE OR REPLACE TABLE lakehouse.gold.daily_user_engagement
USING iceberg PARTITIONED BY (days(event_date))
TBLPROPERTIES ('write.distribution-mode'='hash', 'write.target-file-size-bytes'='134217728')
AS SELECT DATE(event_ts) AS event_date, user_id,
COUNT(*) AS total_events,
COUNT(CASE WHEN event_type='purchase' THEN 1 END) AS purchases,
SUM(CASE WHEN event_type='purchase'
THEN CAST(get_json_object(payload,'$.amount') AS DECIMAL(10,2)) ELSE 0 END) AS total_spend
FROM lakehouse.silver.events
WHERE DATE(event_ts) = CURRENT_DATE - INTERVAL 1 DAY
GROUP BY DATE(event_ts), user_id;
A common mistake is creating too many gold tables too early. Keep silver as the default consumption layer and promote to gold only for a proven query pattern that benefits from pre-aggregation - every gold table is a maintenance liability that must be refreshed, monitored, and kept in sync with business definitions.
Three implementation paths
| Dimension | Databricks (Unity + Delta) | Snowflake (Iceberg Tables) | Open-source (Spark + Iceberg + Nessie + Trino) |
|---|---|---|---|
| Table format | Delta (UniForm for Iceberg compat) | Iceberg (native) | Iceberg |
| Catalog | Unity Catalog (proprietary) | Snowflake-managed (Polaris OSS) | Nessie / Glue / Hive Metastore |
| Streaming | Structured Streaming | Snowpipe Streaming, Dynamic Tables | Flink + Iceberg sink |
| Operational complexity | Low (managed) | Low (managed) | High (you run everything) |
| Vendor lock-in | Medium (UniForm mitigates) | Low-medium (open format, proprietary engine) | None |
| Best for | Heavy ML/notebook teams | SQL-first teams on Snowflake | Strong infra teams wanting full control |
| Est. cost (10TB, moderate load) | $3,000-6,000/mo | $2,500-5,500/mo | $1,500-4,000/mo + engineering time |
Databricks + Unity Catalog is the path of least resistance for teams already in Databricks notebooks needing ML and engineering on one platform - the most integrated governance experience, with automatic lineage and a fast SQL engine that lets many teams drop a separate warehouse. The trade is opaque DBU pricing that escalates without careful cluster management; start serverless and only move to dedicated clusters when you can prove the benefit. Snowflake Iceberg Tables (GA in late 2025) let you store data as Iceberg in your own object storage while querying through Snowflake - lakehouse economics with Snowflake's ecosystem, and your data is in open format from day one so Spark, Trino, or DuckDB can read it later; streaming write performance still trails Databricks. The fully open-source stack (MinIO/S3 + Nessie + Spark + Trino + Flink) gives zero lock-in for teams with strong infrastructure engineers. Nessie deserves special mention: its Git-like branching lets you create a branch, run a pipeline that writes experimental data, verify, and merge - a pull request for your data, which transforms CI/CD in data engineering.
Governance and access control
Governance is where lakehouse projects mature into production systems or collapse into chaos - and the open nature (multiple engines, multiple teams, your own storage) makes it harder than a warehouse where one engine controls all access. The framework that holds up: a namespace strategy of catalog.domain.layer_entity (e.g. lakehouse.marketing.silver_campaigns) that maps to ownership; role-based access at the catalog level with column masking and row filters; data quality as governance, with constraints enforced at write time; and lineage and auditing built in.
GRANT USE SCHEMA ON lakehouse.marketing TO marketing_analyst;
GRANT SELECT ON lakehouse.marketing.gold_campaign_metrics TO marketing_analyst;
GRANT SELECT, MODIFY ON lakehouse.marketing.silver_campaigns TO marketing_engineer;
-- PII masking + regional row filtering
ALTER TABLE lakehouse.marketing.silver_campaigns ALTER COLUMN customer_email SET MASK mask_pii;
ALTER TABLE lakehouse.marketing.silver_campaigns SET ROW FILTER region_filter ON (region);
Data-quality rules belong to governance, not a separate concern - every silver table gets constraints (non-null keys, value ranges, uniqueness, enum membership) enforced through a tool like Great Expectations. For lineage, managed platforms include it; on the open stack, integrate OpenLineage with Spark and Flink so every run emits events to a Marquez instance. Lineage isn't optional - it's the first thing an auditor asks for and the fastest way to debug "why did this number change?"
Cost modeling
The lakehouse's economic promise holds only if you actively manage costs. Tier storage by medallion layer: bronze (highest volume, lowest access - move to Glacier Instant Retrieval or Nearline after 90 days, $0.004-0.010/GB); silver (frequent access, standard storage, $0.020-0.025/GB); gold (small after aggregation, standard storage). Don't forget hidden storage costs - Iceberg metadata files, old snapshots (set retention: 7 days bronze, 30 silver, 90 gold), and orphaned files from failed writes. Schedule table maintenance:
CALL lakehouse.system.expire_snapshots('lakehouse.silver.events', TIMESTAMP '2026-02-13 00:00:00', 100);
CALL lakehouse.system.remove_orphan_files(table => 'lakehouse.silver.events', older_than => TIMESTAMP '2026-02-17 00:00:00');
CALL lakehouse.system.rewrite_data_files(table => 'lakehouse.silver.events', strategy => 'sort', sort_order => 'event_ts ASC NULLS LAST');
Compute is typically 60-80% of total cost, driven by three levers: matching engine to workload (don't run Spark for a 100-row lookup - Trino or serverless SQL for interactive, Spark for heavy batch, Flink for streaming, which can cut compute ~40%); cluster sizing and aggressive auto-scaling with spot instances for fault-tolerant batch; and file-layout optimization, since too many small files or poor partitioning forces 10× the necessary scan volume.
| Category (10TB, mid-size org) | Open-source (AWS) | Databricks | Snowflake |
|---|---|---|---|
| Object storage (mixed tiers) | $180 | $180 | $180 ext / $400 managed |
| Batch compute (4h/day) | $800 (EMR Spark) | $1,400 (Jobs) | $1,200 (XL warehouse) |
| Interactive SQL (8h/day) | $600 (Trino) | $900 (SQL Serverless) | $800 (M warehouse) |
| Streaming (24/7) | $500 (Flink/EKS) | $700 (Structured Streaming) | $600 (Snowpipe Streaming) |
| Total | ~$2,280 | ~$3,330 | ~$2,880-3,100 |
The open-source option looks cheapest on paper, but factor in at least one full-time platform engineer for operations and upgrades. At a loaded $15,000-20,000/month for that engineer, managed services become competitive fast for small-to-mid teams.
Lessons from production
- Start with the catalog, not the compute. The catalog is your contract layer - get namespace design, access policies, and metadata right on day one and swapping engines later is easy; bolt governance on afterward and you'll spend months untangling access.
- Compaction is not optional. Streaming creates thousands of small files; without scheduled
rewrite_data_files(every 4-6 hours, targeting 128-256MB), query performance degrades steadily. - Partition carefully. Over-partitioning is worse than under - 100,000 partitions of 50KB files perform terribly. Start coarse (day, not hour) and add granularity only when queries demand it; Iceberg's hidden partitioning lets you change strategy without rewriting data.
- Treat bronze as immutable. Never update or delete it - ingest corrections and handle them in silver, preserving a complete audit trail and the ability to replay.
- Version transformation logic. Gold must be reproducible from silver, silver from bronze. If you can't re-derive a table from its inputs, you've built a single point of failure.
- Monitor query patterns, not just infrastructure. The most expensive lakehouse problem is queries scanning too much from missing partition pruning or stale statistics - log bytes scanned per query and alert on bad ratios.
Where it's heading
Three trends to watch: universal catalog convergence (Apache Polaris, a partially open-sourced Unity Catalog, and Apache Gravitino competing to be the standard open catalog - one likely winning within 18 months to give a truly engine-agnostic metadata layer); embedded AI in the format layer (table formats beginning to store vector embeddings alongside structured columns for semantic search and RAG directly on lakehouse tables, with Iceberg's variant type a step toward it); and declarative lakehouse management (SQLMesh, dbt with Iceberg materialization, and Databricks Asset Bundles making it possible to define the entire lakehouse - tables, transforms, quality rules, access policies - as version-controlled code, so the lakehouse becomes a deployment artifact rather than a pile of notebooks). The lakehouse isn't a passing trend - it's the pattern the industry is converging on because it solves the problem created by splitting storage into two paradigms. Starting a new data platform in 2026, build a lakehouse; maintaining a warehouse, you're likely already adopting lakehouse patterns whether you call them that or not.




Leave a Comment