Every time a pipeline runs, it faces one choice: rebuild the whole table from scratch, or process only what’s new or changed. It’s easy to assume incremental is always the smart, efficient answer and that full recompute is a lazy shortcut. That assumption is a trap — and knowing when it’s wrong can save you a lot of cost and complexity.
Here’s the honest framing: incremental isn’t automatically better. It’s a trade-off between compute cost and correctness risk, and it comes down to three things — how reliably you can detect changes, how much data you’re processing, and how much “drift” you can tolerate before it becomes a production incident.
The two approaches, plainly
- Full recompute — throw away the target and rebuild it entirely from the source, every run. Dead simple, no moving parts, and impossible to drift because it’s rebuilt fresh each time. The cost grows with the table size.
- Incremental — process only the new or changed rows and merge them in. Much cheaper at scale, but it needs a reliable way to detect what changed, plus extra machinery (watermarks, state, merge logic) — and it can silently drift out of sync with the source.
The key insight most people miss: for a small table, the effort of doing incremental correctly costs more than just rebuilding it. Change-detection logic isn’t free.
The decision tree
Work down these questions and stop at the first “yes.”

- Is the source immutable and append-only? (event logs, raw ingestion) → Incremental. Nothing ever changes, so you just process new files — no correctness risk, no change-detection needed.
- Is the table under ~50 GB and does a full recompute finish in under ~15 minutes? → Full recompute. At this size the compute is cheap, and rebuilding is far simpler than maintaining incremental logic. Immune to drift, easy to reason about.
- Can the upstream data be corrected or retroactively updated? → Full recompute, or incremental plus a periodic full reload. Pure incremental silently misses historical corrections, and that drift is hard to spot.
- Is the source a Delta table with Change Data Feed (CDF) enabled? → Incremental via CDF. CDF hands you exact row-level changes, so change detection is reliable with no guesswork.
- Otherwise (large, mutable source, change detection unclear) → Incremental daily + weekly full recompute. Daily incremental for freshness; a weekly full rebuild to catch any drift before it compounds.
The trap: assuming incremental is always better
This is the mistake to avoid. Incremental sounds more impressive, so people reach for it by default — and full recompute can feel too simple to be “right.” In reality, incremental adds change-detection logic, state management, watermark tracking, and silent-drift risk. For a table under 50 GB, the compute you save is usually wiped out by the engineering cost of keeping it correct.
The real skill: recognizing when the more complex approach is actually over-engineering. Simplicity has real value — a rebuild you can trust beats a clever incremental pipeline you’re always debugging.
How do you detect what changed? (this decides everything)
Incremental lives or dies on your ability to reliably answer “which rows changed since last run?” There are three tiers, best to worst — and the quality of your change signal is what really moves the recompute-vs-incremental line.
Best: Change Data Feed (CDF)
If the source is a Delta table with CDF enabled, it records exactly which rows were inserted, updated, and deleted. You read them directly — no guessing, no diffing.
-- exact row-level changes since version 42
SELECT * FROM table_changes('retail.silver.orders', 42);
-- each row carries a _change_type: insert / update / deleteGood enough: a reliable timestamp (watermark)
No CDF, but the table has a trustworthy updated_at column? Pull only rows newer than the last run and merge them in. This is the classic watermark pattern.
new_rows = (spark.read.table("retail.bronze.orders")
.filter("updated_at > '{}'".format(last_run_ts)))
# then MERGE new_rows into the target tableThe risk: if any row is updated without touching updated_at, you silently miss it. That gap is precisely why CDF exists.
Worst: no change signal → diff the whole thing
If there’s no CDF and no reliable timestamp, your only option is to compare the entire source against the target on the primary key to find differences. That costs almost as much as a full recompute — so at that point “incremental” is a misleading label; you’re doing nearly all the work of a rebuild anyway. Often the honest answer here is: just do the full recompute.

The silent killer: drift
Incremental pipelines have a nasty failure mode: they slowly drift out of sync with the source and nothing errors out. A retroactive correction upstream gets missed, or a watermark skips a window, and your target is quietly 2% wrong — until someone notices a report doesn’t match.
The defense is a reconciliation check that periodically compares source and target. The simple version counts rows per partition and flags mismatches; the robust version checksums key columns on a primary-key join to pinpoint the drifted rows.
# simplest drift check — row counts per day should match the source
src = spark.read.table("retail.bronze.orders").groupBy("order_date").count()
tgt = spark.read.table("retail.silver.orders").groupBy("order_date").count()
# alert on any day where the counts differ beyond a small thresholdFix small drift with a targeted MERGE; fix widespread drift with a full recompute — then find the root cause (usually a missed retroactive update or a skipped watermark window). This is exactly why the “incremental daily + weekly full recompute” pattern is so popular: the weekly rebuild is a built-in drift reset.
A quick worked example
Three tables in the same retail platform, three different answers:
- Raw order events — append-only, nothing ever changes → Incremental (process new files, zero risk).
- A 3 GB store dimension — small, rebuilds in a minute → Full recompute (why maintain change logic for a table this size?).
- A big sales fact where the source can be corrected weeks later → Incremental daily + weekly full recompute (fresh daily, drift-proofed weekly).
The takeaway
Rebuild or process-only-new is a trade-off between compute cost and correctness risk — not a mark of how good you are. Use incremental when the source is append-only, or when it’s a Delta table with Change Data Feed giving you reliable changes. Use full recompute when the table is small (under ~50 GB), when upstream data can be retroactively corrected, or when there’s no dependable change signal — because it’s simpler and immune to drift. For big, mutable sources, run incremental daily with a weekly full recompute to get freshness and a drift safety net. And whenever you go incremental, add a reconciliation check — silent drift is the bug you won’t see coming.
Frequently Asked Questions
When is full recompute better than incremental processing?
Full recompute is better when the table is small (roughly under 50 GB and rebuilds in minutes), when upstream data can be corrected retroactively, or when there’s no reliable way to detect changes. In these cases it’s simpler, immune to drift, and often cheaper than maintaining incremental change-detection logic and state.
When does incremental processing cost more than full recompute?
When the change-detection overhead exceeds the compute saved. That happens when the table is small enough that a rebuild is cheap anyway, when there’s no reliable change signal so you must diff the whole dataset, or when the watermark/checkpoint/merge machinery costs more engineering time than the cluster cost of a rebuild. The breakeven is around 50 GB but shifts with how clean the change signal is.
How do I detect which rows changed since the last run?
Best case, the source is a Delta table with Change Data Feed enabled and table_changes() gives you exact inserts, updates, and deletes. Next best, a reliable updated_at timestamp lets you filter rows newer than the last run. Worst case, with no change signal you must diff the full source against the target on the primary key — which costs about as much as a full recompute.
What is data drift in an incremental pipeline?
Drift is when an incremental target slowly falls out of sync with the source without any error — usually because a retroactive upstream correction was missed or a watermark skipped a window. Because nothing fails loudly, it’s often caught only when a report looks wrong. A reconciliation job that compares source and target counts or checksums is how you detect it.
What is the “incremental daily + weekly full recompute” pattern?
It’s a hybrid for large, mutable sources: run incremental every day for freshness and low cost, and do a full recompute once a week to catch any drift or missed corrections before they compound. The weekly rebuild acts as a built-in reset, giving you most of incremental’s savings with a safety net against silent drift.
How do I detect changes if the Delta source has no Change Data Feed?
Fall back to a high-watermark pattern using a reliable timestamp column: filter rows where updated_at is greater than the last run time and merge them in. The risk is that any row updated without changing that timestamp is silently missed. If no reliable timestamp exists, you’re forced to diff the full source against the target, which costs nearly as much as a full recompute.





