Advantages of Lakeflow Spark Declarative Pipelines for ETL
Why declarative pipelines beat hand-written Spark jobs for production ETL. Lakeflow Spark Declarative Pipelines — the framework formerly known as Delta Live Tables — lets you declare what your pipeline should produce and hands the orchestration, retries, quality checks, and incremental processing to the engine.
📝 Naming update — read this first
Delta Live Tables (DLT) has been renamed Lakeflow Spark Declarative Pipelines (SDP). The engine was also open-sourced into Apache Spark 4.1 as Apache Spark Declarative Pipelines; the Databricks product (Lakeflow SDP) extends it and runs on the Databricks Runtime. The Python API moved too: the old import dlt / @dlt.table is now from pyspark import pipelines as dp / @dp.table. The dlt module still works but is deprecated. You may still see "DLT" everywhere in the wild — treat the terms as the same thing.
01Two ways to build a pipeline
There are two fundamentally different ways to build an ETL pipeline on Databricks. The imperative way — hand-written Spark and Structured Streaming jobs — means you write every step yourself: the reads, the transformations, the checkpoints, the write logic, the orchestration order, the retry handling, the data-quality checks, and the monitoring. You describe how everything happens.
The declarative way — Lakeflow Spark Declarative Pipelines — flips this. You declare the datasets you want to exist and the queries that define them, and the engine works out the rest: dependency order, parallelism, incremental computation, retries, and quality enforcement. You describe what you want, not how to get there.
Imperative code tells the machine every step. Declarative code tells the machine the destination and lets it find the route.
🎯 Exam focus
The exam wants you to recognise why the declarative approach is preferred for production ETL. The headline advantages to memorise: automatic orchestration, built-in data quality (expectations), automatic retries, incremental processing, unified batch + streaming, and free lineage/observability — all of which you would otherwise build by hand.
02What Lakeflow SDP actually is
Lakeflow Spark Declarative Pipelines is a declarative framework for building batch and streaming pipelines in SQL or Python. You define a small number of object types, and the pipeline runtime assembles and runs them:
- Streaming tables — Unity Catalog-managed tables that are streaming targets, ideal for incremental ingestion (e.g. via Auto Loader).
- Materialized views — UC-managed tables that hold the results of a batch query and recompute efficiently when inputs change.
- Flows — the processing steps that move data into a table (append flows, AUTO CDC flows).
- Sinks — targets for writing streaming results out to external systems such as Kafka or Event Hubs.
- Expectations — declarative data-quality constraints attached to a dataset.
A pipeline is the container for all of these. When it runs, it analyses the dependencies between your datasets, builds an execution graph, and orchestrates the whole thing automatically.
📝 Note — SDP vs. Apache Spark Declarative Pipelines
The declarative engine is now part of open-source Apache Spark 4.1 (via the pyspark.pipelines module). Lakeflow SDP is the Databricks product that extends it with managed features and runs on the Databricks Runtime — and it requires the Premium plan. Code written against the open-source module runs unmodified on Databricks.
03Declarative vs. imperative — the core shift
The single biggest advantage is captured in one number: SDP can reduce hundreds or thousands of lines of manual Spark and Structured Streaming code down to a handful. Compare the same Bronze→Silver step written both ways.
# You wire up reads, checkpoints, MERGE logic, and orchestration yourself def upsert_to_silver(batch_df, batch_id): batch_df.createOrReplaceTempView("updates") batch_df.sparkSession.sql(""" MERGE INTO silver.orders t USING updates s ON t.order_id = s.order_id WHEN MATCHED THEN UPDATE SET * WHEN NOT MATCHED THEN INSERT *""") (spark.readStream.format("cloudFiles") .option("cloudFiles.format", "json") .load("/landing/orders/") .filter("amount >= 0") # hand-rolled quality check .writeStream .foreachBatch(upsert_to_silver) .option("checkpointLocation", "/chk/silver") # you manage state .trigger(availableNow=True) .start()) # ...then separately schedule + order every job, add retries, add monitoring
from pyspark import pipelines as dp @dp.table(name="silver_orders") # declares a streaming table def silver_orders(): return (spark.readStream.table("bronze_orders") .filter("amount >= 0")) # orchestration, checkpoints, retries, lineage — all handled for you
💡 The load-bearing analogy
Hand-written Spark is driving with paper maps — you plan every turn, and if a road is closed you re-plan by hand. SDP is satnav — you enter the destination, and it computes the route, reroutes around failures, and tells you if you've gone wrong. Both get you there; only one does the thinking for you.
04Advantage 1 — automatic orchestration
With hand-written jobs, you decide the order things run in — Bronze before Silver, Silver before Gold — usually by wiring tasks together in a scheduler. Get the order wrong, or forget a dependency, and the pipeline breaks or produces stale data.
SDP reads the queries you declared, works out the dependency graph automatically, and runs each step in the correct order and with the maximum safe parallelism. Because a Silver table reads from a Bronze table, SDP simply knows Bronze must run first — you never declare the order explicitly.
- The datasets you want (tables & views)
- The query that defines each one
- The data-quality rules that must hold
- Dependency order & parallel execution
- Incremental compute & checkpoints
- Retries, lineage, and quality metrics
05Advantage 2 — built-in data quality
In hand-written pipelines, data-quality checks are something you bolt on manually — a filter here, a count there, a custom alert somewhere else. SDP makes quality a first-class, declarative feature through expectations: constraints you attach to a dataset that are enforced and tracked automatically.
You choose what happens when a row violates a rule: keep it and record the metric, drop the bad row, or fail the update entirely. The pass/fail rates are captured in the pipeline's event log, so quality is observable, not guesswork.
CREATE OR REFRESH STREAMING TABLE silver_orders ( -- drop rows that fail; violations tracked as metrics CONSTRAINT valid_amount EXPECT (amount >= 0) ON VIOLATION DROP ROW, CONSTRAINT valid_order_id EXPECT (order_id IS NOT NULL) ON VIOLATION FAIL UPDATE ) AS SELECT * FROM STREAM(bronze_orders);
🎯 Exam focus
Expectations are a favourite exam topic. Know the three violation actions: the default (retain the row, record the metric), DROP ROW (discard failing rows), and FAIL UPDATE (halt the pipeline). This is quality enforcement you'd otherwise have to hand-build.
06Advantage 3 — incremental & unified batch/stream
Efficient pipelines process only new data, not the entire dataset every run. Doing that by hand means managing checkpoints, offsets, and state yourself — exactly the kind of thing that's easy to get subtly wrong.
- Streaming tables incrementally ingest and process new rows as they arrive, tracking progress automatically.
- Materialized views recompute their results efficiently when upstream data changes, rather than from scratch.
- One framework for both: the same SDP code handles batch and streaming — the difference is often just
spark.readvs.spark.readStream. You don't maintain two separate codebases.
💡 Why this matters for cost
Incremental processing means a nightly pipeline touches only the day's new records, not the full history. Over a large table that's the difference between minutes and hours — and a proportional saving on compute.
07Advantage 4 — automatic retries & recovery
Transient failures — a flaky network call, a briefly unavailable source — are a fact of production life. In hand-written jobs you write your own retry logic and hope you covered every case. SDP retries transient failures automatically, and it does so from the most granular, cheapest unit first: it retries the failed Spark task, then the flow, and only if needed the whole pipeline.
Combined with checkpoint-based state, this means a pipeline can recover from an interruption and pick up where it left off — without you writing a single line of recovery code.
📝 Note — resilience is the default
Because the runtime owns state and retries, production reliability is something you configure, not something you engineer. That's a large chunk of operational code you simply never write.
08Advantage 5 — lineage, observability & governance
Because SDP knows every dataset and every dependency, it produces a live pipeline graph and end-to-end lineage for free. You can see exactly how Gold traces back to Silver traces back to Bronze — no separate lineage tooling required.
- Automatic lineage: the dependency graph is visible in the UI and captured in Unity Catalog.
- Observability: a structured event log records runs, data-quality metrics, and failures.
- Governance built in: streaming tables and materialized views are Unity Catalog-managed tables, so permissions, auditing, and lineage come through UC natively.
🎯 Exam focus
Remember that SDP outputs — streaming tables and materialized views — are always Unity Catalog-managed tables. This is why governance and lineage are automatic, and it's a common exam detail.
09Advantage 6 — CDC & SCD without hand-rolled MERGE
Change data capture and slowly changing dimensions are notoriously fiddly to implement by hand — you juggle MERGE statements, ordering columns, and out-of-order events. SDP provides AUTO CDC (the successor to APPLY CHANGES) to handle this declaratively, including both SCD Type 1 (overwrite with latest) and SCD Type 2 (retain full history).
You point AUTO CDC at a source of changes, tell it the key and the sequencing column, and it maintains the target dimension correctly — deduplicating, ordering, and applying inserts, updates, and deletes for you.
📝 Note — the naming, again
You'll see this called APPLY CHANGES INTO in older material and AUTO CDC in current docs; the Python function moved from dlt.apply_changes to dp.create_auto_cdc_flow. Same capability, newer name.
10When hand-written Spark still wins
Declarative isn't a silver bullet, and a good architect knows the edges. Hand-written Spark still makes sense when:
- You need fine-grained control the framework doesn't expose — custom state handling, unusual sinks, or bespoke streaming logic.
- The workload isn't really a table-producing pipeline — one-off scripts, complex ML training loops, or arbitrary Python that doesn't fit the dataset model.
- You can't use the Premium plan or the managed runtime SDP requires.
- You're integrating with external orchestration where you deliberately want to own the control flow.
For the large majority of production ETL that reads sources, transforms through Bronze/Silver/Gold, and enforces quality, SDP removes far more work than it costs — which is exactly why it's the recommended default.
11SDP vs. hand-written Spark
| Concern | Lakeflow SDP | Hand-written Spark |
|---|---|---|
| Approach | Declarative (what) | Imperative (how) |
| Orchestration | Automatic from dependencies | You wire it up manually |
| Data quality | Built-in expectations | Hand-coded checks |
| Incremental processing | Automatic (STs & MVs) | You manage checkpoints/state |
| Retries & recovery | Automatic, task→flow→pipeline | You write retry logic |
| Lineage & monitoring | Generated for free | Separate tooling |
| CDC / SCD | AUTO CDC (SCD 1 & 2) | Manual MERGE |
| Code volume | A few lines | Hundreds to thousands |
| Best for | Most production ETL | Custom / non-standard logic |
12Exam tips & practice questions
Key facts to memorise
- SDP = declarative: you declare datasets; the engine handles orchestration, retries, incremental compute, and quality.
- Naming: Delta Live Tables → Lakeflow Spark Declarative Pipelines; open-sourced in Apache Spark 4.1.
- Objects: streaming tables, materialized views, flows, sinks, expectations — inside a pipeline.
- Outputs are UC-managed tables → automatic governance and lineage.
- Expectations = declarative quality: default /
DROP ROW/FAIL UPDATE. - AUTO CDC (formerly APPLY CHANGES) handles SCD Type 1 & 2 declaratively.
- Requires the Premium plan.
- A.It only runs on all-purpose clusters
- B.The engine handles orchestration, retries, and incremental processing automatically
- C.It eliminates the need for Delta Lake
- D.It requires no compute at all
Reveal answer
Declarative pipelines let you declare the datasets you want while the engine manages orchestration, retries, checkpoints, and incremental computation — the work you would otherwise write by hand.
- A.Checkpoints
- B.Expectations
- C.Broadcast joins
- D.Instance pools
Reveal answer
Expectations are declarative constraints (e.g. EXPECT (amount >= 0)) that the pipeline enforces and tracks automatically, with a configurable action on violation.
- A.The engineer must specify the order in a config file
- B.Alphabetical ordering of table names
- C.SDP infers the dependency from the query and orchestrates automatically
- D.It runs all tables simultaneously regardless of dependencies
Reveal answer
SDP analyses the queries, discovers that Silver depends on Bronze, and builds the execution graph automatically — you never declare the order explicitly.
- A.ON VIOLATION FAIL UPDATE
- B.ON VIOLATION DROP ROW
- C.ON VIOLATION RETRY
- D.The default (no action specified)
Reveal answer
DROP ROW discards failing rows and continues. FAIL UPDATE halts the pipeline; the default keeps the row and records the violation as a metric. There is no RETRY violation action.
- A.External tables and temporary views; no governance
- B.Streaming tables and materialized views; both are Unity Catalog-managed tables
- C.Global temp views and RDDs; both are session-scoped
- D.CSV files and JSON files; both are unmanaged
Reveal answer
Streaming tables and materialized views are the core outputs, and both are Unity Catalog-managed tables — which is why lineage and governance are automatic.
- A.It immediately restarts the whole pipeline from scratch
- B.It retries from the most granular unit first: task, then flow, then pipeline
- C.It stops and waits for manual intervention
- D.It skips the failed data permanently
Reveal answer
SDP retries the cheapest, most granular unit first — the Spark task — escalating to the flow and finally the whole pipeline only if needed. This makes recovery both automatic and cost-efficient.
- A.AUTO CDC (formerly APPLY CHANGES)
- B.A broadcast hint
- C.Z-ordering
- D.The VACUUM command
Reveal answer
AUTO CDC (the successor to APPLY CHANGES) applies inserts, updates, and deletes declaratively and supports both SCD Type 1 and Type 2, handling ordering and deduplication for you.
- A.SDP is entirely proprietary with no open-source counterpart
- B.The declarative engine is open-sourced in Apache Spark 4.1; Lakeflow SDP extends it on the Databricks Runtime
- C.SDP replaces Apache Spark completely
- D.SDP only works outside of Databricks
Reveal answer
Apache Spark Declarative Pipelines is part of open-source Spark 4.1 (the pyspark.pipelines module). Lakeflow SDP is the Databricks product that extends it with managed features and runs on the Databricks Runtime.
- A.A standard Bronze→Silver→Gold ETL pipeline with quality checks
- B.Incremental ingestion from cloud storage into a managed table
- C.A workload needing bespoke custom state handling the framework doesn't expose
- D.Maintaining an SCD Type 2 dimension
Reveal answer
Declarative pipelines excel at standard table-producing ETL (A, B, D). Hand-written Spark is justified when you need low-level control the framework doesn't offer, such as custom state management or unusual streaming logic.
13Quick-reference cheat sheet
Declare what datasets should exist; the engine handles how — orchestration, retries, incremental compute, quality.
DLT → Lakeflow SDP. Open-sourced in Apache Spark 4.1. API: from pyspark import pipelines as dp.
Streaming tables (incremental) · materialized views (batch) · flows · sinks · expectations.
Expectations: default (track) · DROP ROW · FAIL UPDATE. Metrics in the event log.
Dependencies inferred from queries → correct order + max parallelism, automatically. Granular auto-retries.
Outputs are UC-managed (free lineage). AUTO CDC handles SCD 1 & 2. Premium plan required.
That's Subdomain 3.3. The one-line takeaway: declarative pipelines let you describe the destination, not the route. For standard production ETL — ingest, clean through Bronze/Silver/Gold, enforce quality, serve — Lakeflow SDP removes the orchestration, retry, incremental, quality, and lineage code you'd otherwise write and maintain by hand. Know the six advantages, know the objects, and remember the naming has changed but the concepts haven't.
Good luck on the exam!