Data Pipeline Architecture and Design Questions
End-to-end design of data pipelines: source-to-sink flow, staging layers, idempotency, backfills, and reprocessing. Covers choosing between batch and streaming stages, decoupling ingestion from transformation, and designing for evolvability. The foundational systems-design skill for a data engineering interview.
A validation check could either block bad records from moving further downstream, or just let them through and raise an alert. For a pipeline feeding a dataset other teams depend on, how do you decide which to do, and where in the pipeline would you put that check?
Sample Answer
Direct answer
Decide by weighing the cost of a false block (delaying or dropping a probably-fine record) against the cost of a false pass (letting a genuinely bad record reach every consumer of a shared dataset), not by a single fixed rule. Checks on a hard, structural guarantee that downstream logic actually depends on, like a required key being present or a value violating a constraint the rest of the pipeline assumes, should block. Checks that are really statistical health signals, like a metric drifting outside a normal range, should let the record through and raise an alert instead. Placement follows the same logic: put blocking checks as early as possible, before the data reaches anything shared, and put statistical checks wherever there's enough context to compute a meaningful baseline, which is often necessarily later.
Structured elaboration
Decision criteria, block versus alert-and-pass:
| Factor | Favors blocking | Favors alert-and-pass |
|---|---|---|
| Blast radius | Many downstream teams depend on this exact field or table | A single, low-stakes internal consumer |
| Nature of the violation | A hard, structural guarantee downstream logic assumes (non-null key, referential integrity, schema type) | A soft, statistical signal (an out-of-range value, a distribution shift) that could still be legitimate |
| Confidence in the check | Deterministic, unambiguous | Probabilistic or threshold-based, prone to false positives |
| Cost of being wrong | Reprocessing a false block is usually cheap once the pipeline can be trusted again | Letting through a false failure that's already mixed into a shared aggregate is often expensive or impossible to fully unwind |
Placement follows the same reasoning, not convenience. Hard, structural, cheap-to-evaluate checks belong as early as possible, ideally at ingestion, before a record can be joined, aggregated, or shared with anyone, because catching a violation before it's mixed into downstream state is strictly cheaper than unwinding it afterward. Statistical checks (drift, anomaly detection against a baseline) often need enough accumulated data to be meaningful, which can genuinely push them later in the pipeline; their outcome should still go to an alert rather than a block, since blocking on a probabilistic signal risks stalling the pipeline on a false positive.
A useful middle option: quarantine-and-continue. For a check that's borderline, confident enough to worry about but not confident enough to trust blindly, setting the record aside (not passed downstream, not silently dropped) while the rest of the batch proceeds avoids both a hasty block and a silent pass.
What kinds of checks tend to fall where. A null check on a required key or a duplicate-key check is usually the hard, deterministic, blocking kind. A range or distributional check on a numeric field is usually the softer, alert-worthy kind, precisely because it's inferring "unusual" rather than checking a fact the schema guarantees.
Worked example
A shared orders table feeds 12 downstream consumers. A schema check finds 0.4 percent of a day's 1,000,000 incoming records missing the required customer identifier, a value every downstream join assumes is present:
1,000,000×0.004=4,000 affected rowsEven at a small fraction of the batch, this is treated as blocking, because those 4,000 rows would otherwise silently break joins for all 12 downstream consumers at once, and the check itself is unambiguous (the key is either present or it isn't).
Separately, a statistical check on average order value shows a shift from a baseline mean of $85 to $102:
85102−85≈20% deviation from baselineThis could be a genuine promotion-driven spike or a data problem; it goes to alert-and-pass rather than blocking, because the check is probabilistic (not every deviation is a defect) and the cost of being wrong in the blocking direction (halting real data from a legitimate business event) is worse here than the cost of a few hours where a chart looks slightly off before a human confirms it either way.
flowchart LR
A[Raw ingestion] --> B[Hard structural checks: block on failure]
B -->|pass| C[Transform and aggregate]
C --> D[Statistical or drift checks: alert, let pass]
D --> E[Shared dataset]
E --> F[Downstream consumers]
B -->|fail| G[Quarantine, do not propagate]
Trade-offs & pitfalls
- Blocking on every check, including soft statistical ones, trades away data availability for a false sense of safety, and will eventually stall the pipeline on a legitimate anomaly like a real traffic spike.
- Letting every check through as alert-only, including hard structural violations, means a genuinely broken record (a missing key that breaks every downstream join) propagates to all 12 consumers before anyone even reads the alert.
- Placing every check right before a report renders catches problems far too late, after the data has already been joined and aggregated into shared state that's expensive to unwind.
- A common wrong turn is picking one block-or-alert policy for the whole pipeline instead of deciding per check, based on that specific check's own confidence and blast radius.
Once a pipeline has multiple dependent stages, what does a workflow orchestrator actually give you that a plain cron job doesn't?
Sample Answer
Direct answer
A cron job only knows a clock time; it has no idea whether the upstream stage it depends on actually finished, failed, or is still half-written, so a multi-stage pipeline built on cron entries is really a set of independent guesses about how long each stage takes. A workflow orchestrator (Airflow, Dagster, Prefect and similar) models the pipeline as a directed acyclic graph (DAG) of tasks with explicit dependency edges, so a downstream task only starts when its upstream task actually reports success, and the system gives you retries, backfills, and one place to see the whole pipeline's health for free.
Structured elaboration
Dependency-awareness versus time-awareness. Cron triggers on wall-clock time alone. If stage two is scheduled 30 minutes after stage one, that gap is a guess about stage one's runtime, not a guarantee. An orchestrator's DAG edge says "task B runs after task A succeeds," which is true regardless of how long A actually takes that day.
Failure propagation. In a DAG, if a task fails, its downstream dependents simply do not run (or run a defined failure branch), automatically. With cron, each stage fires on its own schedule no matter what happened upstream, so a failed stage one silently lets stage two start against stale or partial data.
Retries with policy. Orchestrators let you attach a retry policy per task (bounded attempts, backoff), so a transient failure recovers without a human noticing. A cron job that fails just waits for its next scheduled fire, however far away that is.
Backfill and replay. Because tasks are parameterized by the DAG's logical run (a date or partition), you can rerun a single historical slice through the whole dependency chain. Cron has no concept of "this stage, for that date," so backfilling means manually re-invoking scripts in the right order.
Observability. A DAG-based orchestrator gives one UI or API surface for task duration, success/failure history, and where in the chain something is stuck. Cron gives you whatever each script happens to log, scattered across machines and cron logs.
When cron is still the right tool. A single, independent job with no downstream dependents and no need for backfill or retry policy does not need a DAG orchestrator; adding one there is pure overhead.
Worked example
Take a 3-stage pipeline: extract, transform, load. With cron, each stage gets a fixed offset:
extract=01:00,transform=01:30,load=02:00This assumes extract always finishes within 30 minutes. If the source is slow once and extract takes 45 minutes, transform still fires at 01:30 and reads a half-written file; cron has no signal that would stop it. Generalizing to a chain of N stages, cron needs N independently tuned fixed offsets, each one a manual guess that has to be re-tuned whenever runtime varies:
cron offsets needed=N,DAG dependency edges needed=N−1For N=5: cron requires 5 separately maintained time guesses; the DAG requires 4 edges, and those edges stay correct no matter how long any one stage takes, because the trigger is "the previous task instance succeeded," not a clock offset.
Retries make the gap sharper. Suppose a task fails transiently (a brief network blip to the source). An orchestrator retries with exponential backoff, say attempts at 1, 2, and 4 minutes after failure:
1+2+4=7 minutes until the task is resolved or escalatedA cron-based stage that fails has no retry concept built in: the next opportunity to reprocess is the next scheduled fire, often the next day:
24×60=1440 minutes before the same job runs againThat gap, 7 minutes versus 1440 minutes, is the practical cost of "no retry semantics," not an abstract one.
Trade-offs & pitfalls
An orchestrator is another system to run and keep healthy (unless you use a managed offering), so standing one up for a single independent job with no dependents is pure overhead with no payoff. Sensor or polling patterns (a task that waits by repeatedly checking for a file or a signal) can add their own latency or cost if the poll interval is misconfigured, so "dependency-aware" isn't automatically "instant." A common wrong turn is putting heavy transformation logic directly inside orchestrator tasks instead of having the orchestrator call out to dedicated compute; the orchestrator's job is to sequence and observe work, not to do the work itself.
What does the write-audit-publish pattern mean for a pipeline's data quality, and what problem does inserting an audit step before publish actually solve?
Sample Answer
Direct answer
Write-audit-publish means landing new data in a staging location consumers can't see yet, running data-quality checks (the audit) against it while it is still invisible, and only then making it visible with a single atomic operation (a partition swap or a pointer flip), rather than publishing first and validating afterward. The audit step exists to solve the problem of consumers reading bad or partial data during a load: without it, "load then check" means anyone querying during or right after a bad load already saw the wrong numbers before any check had a chance to catch it.
Structured elaboration
The problem this solves. A pipeline that writes directly into the table or partition consumers actually query, then validates afterward if at all, leaves a window between when writes start and when a check would have failed, during which consumers can read incomplete or wrong data with no visible signal anything is wrong.
The three stages:
- Write. Land the new batch in an isolated location, a staging table, a new partition or version, a new file set, that existing queries against the published location cannot see at all.
- Audit. Run validation against that staged data while it remains invisible to consumers: row counts against expectation, null rates, schema conformance, referential checks, business-rule checks.
- Publish. Only if the audit passes, flip visibility with a single atomic operation, an atomic partition swap, a metadata pointer update, or a table rename, so consumers see either the previous, already-audited state or the new, now-audited state, never a partial mix of the two.
Why the publish step has to be atomic. If publishing itself were multiple steps (remove old rows, then insert new ones), a reader querying in the middle could see an inconsistent partial state even after the audit already passed. The pattern only fully solves the visibility problem if the very last step is a single, indivisible flip.
What happens when the audit fails. The staged batch is simply never published. Production keeps serving the last good, already-audited state, and the failed batch goes to remediation instead of ever becoming visible to anyone.
Where it fits and where it doesn't. This suits batch or micro-batch loads into a location with many downstream readers who can't each validate before consuming, like a shared warehouse table. Strict low-latency streaming, where staging and swapping an entire batch isn't practical, achieves the same underlying goal (don't let one bad unit block or corrupt everyone) through per-record dead-letter handling instead.
Worked example
A daily table normally holds 2,000,000 rows. A join bug in that day's load causes fanout, and the staged batch lands with 2,600,000 rows:
2,000,0002,600,000−2,000,000=30% overcountIf the audit's row-count check allows a tolerance band of plus-or-minus 5 percent around the historical baseline, a 30 percent deviation fails it clearly, so the atomic swap never happens. Consumers keep reading yesterday's correct 2,000,000-row table while the bad batch sits quarantined and the join bug gets fixed, instead of every downstream report briefly, or permanently until someone happens to notice, reflecting a 30 percent inflated count.
flowchart LR
A[New batch written to staging] --> B[Audit checks run on staged data]
B -->|pass| C[Atomic publish: swap or pointer flip]
B -->|fail| D[Staged data quarantined]
C --> E[Consumers read new data]
D --> F[Remediation and retry]
F --> A
Trade-offs & pitfalls
- Auditing the already-published table instead of the staged one only catches the problem after consumers may already have read bad data; the ordering, audit before publish rather than after, is the entire point of the pattern.
- Making publish itself a multi-statement operation reintroduces the exact partial-visibility problem the pattern exists to prevent.
- The pattern trades some latency (data isn't visible until the audit finishes) and some storage (staged and published copies briefly coexist) for that safety; that's a clear win for a widely-consumed shared table, and a less obvious win for a low-stakes, single-consumer dataset where the extra latency may not be worth it.
- A common wrong turn is treating "we run some validation somewhere in the pipeline" as equivalent to write-audit-publish; the pattern specifically depends on the audited data being invisible to consumers until it passes, not just checked at some point along the way.
Design a CDC pipeline to replicate a set of OLTP tables into a data warehouse. How do you handle the initial full snapshot versus ongoing incremental changes, and how do you keep the target consistent if the source schema changes underneath you?
Sample Answer
Direct answer
Bootstrap the target with a consistent point-in-time snapshot of each source table, and hand off to the change data capture (CDC) stream at the exact log position the snapshot was taken from, so the union of "snapshotted rows" and "streamed changes from that position onward" covers every row exactly once, with no gap and no duplicate window. Keep the target schema evolvable by treating additive changes (a new nullable column) as automatic, and gating anything that changes existing meaning, renames, type changes, drops, behind an explicit migration step instead of silently applying whatever the source does next.
Structured elaboration
Snapshot-to-stream handoff. Take the snapshot inside a transaction, or using the database's consistent-snapshot mechanism, and record the exact log position (an LSN, binlog offset, or global transaction identifier) at that instant. Start the CDC stream from that same recorded position. This is the one design decision that determines correctness: pin it wrong and you get either duplicated rows (stream restarts before the snapshot's true cutoff) or silently missing rows (stream starts after it).
Ongoing incremental changes. Log-based CDC (the same mechanism used for capture generally) streams inserts, updates, and deletes keyed by the source's primary key; apply them to the target as upserts, or soft-deletes for deletions, keyed by that same primary key, so replaying or reordering events within one key stays safe.
Schema evolution policy. Additive changes (a new nullable column) propagate automatically because they cannot break anything already relying on the existing shape. Anything that changes existing meaning, a rename, a type narrowing, a column drop, is not auto-applied: the pipeline diffs the schema on each batch, halts propagation for that table when it detects an incompatible change, and requires an explicit versioned migration mapping old to new before resuming. Silently applying a rename as if it were a new column would quietly corrupt every downstream query relying on the old name.
Consistency during migration. The target should never expose a half-migrated schema to readers. A staged, versioned table swap, build the new shape alongside the old, validate it, then atomically point consumers at it, keeps every query against a single, fully-consistent version at any moment.
flowchart LR
A[Source OLTP tables] --> B[Snapshot extractor]
A --> C[Log tailer from pinned position]
B --> D[Target: bootstrap load]
C --> E[Change stream: upsert/soft-delete]
D --> E
E --> F[Target: steady state]
A --> G[Schema diff check]
G -->|additive| E
G -->|breaking change| H[Halt + versioned migration]
H --> E
Worked example
Forty OLTP tables need replication; the largest, orders, has 200 million rows averaging 300 bytes each pre-compression:
If the snapshot pull sustains 200 MB/s:
200 MB/s60,000 MB=300 s=5 minutesCDC starts buffering changes from the recorded log position the moment the snapshot begins. If the source sustains 50 writes/sec against orders during that 5-minute window:
Those 15,000 buffered events are applied, in log order, once the snapshot lands, closing the gap with nothing missed and nothing duplicated. If orders is being replicated on its own as a narrower, near-real-time single-table copy rather than as one of forty tables, the same arithmetic scopes down directly: a 60 GB bootstrap followed by roughly 15,000 buffered upserts to reach steady state, with the target consistent as soon as that buffered replay finishes.
Trade-offs & pitfalls
The single most common corruption source is taking the snapshot without pinning the exact log position, or using an isolation level that doesn't guarantee one consistent read point across the whole table, either duplicating rows if the stream restarts before the true cutoff, or dropping rows if it starts after. Auto-applying every schema change, including renames, is a shortcut that eventually corrupts a column's meaning silently, by the time it's noticed, downstream queries have been reading the wrong thing for a while. Very large tables make a single-transaction snapshot expensive and lock-heavy on the source; a chunked, parallel snapshot by primary-key range trades a slightly more complex bootstrap for not holding a long-lived lock.
What's the difference between a data lake and a data warehouse, and how would a pipeline typically use each as it moves data from source to sink?
Sample Answer
Direct answer
A data lake stores raw and semi-structured data cheaply with schema-on-read, optimized for ingest scale and flexible reprocessing; a data warehouse stores curated, modeled data with schema-on-write, optimized for fast, governed, repeated SQL access. In a source-to-sink pipeline, the lake is typically where raw data lands first and stays as the reprocessing archive, while the warehouse (or a warehouse-modeled zone) is the layer BI tools and dashboards actually query.
Structured elaboration
Core distinction
| Aspect | Data lake | Data warehouse |
|---|---|---|
| Schema | Schema-on-read | Schema-on-write |
| Data shape | Raw, semi- or unstructured, any format | Curated, structured, modeled |
| Cost profile | Cheap object storage, compute billed separately | Storage and compute often tuned together for repeat queries |
| Primary consumer | Engineers and scientists, batch reprocessing | Analysts and BI tools, repeated dashboard queries |
| Query performance | Variable, needs a compute engine layered on top | Tuned for fast, repeated, well-modeled queries |
| Governance | Needs active governance or becomes unmanageable | Governance is largely built into the modeled schema |
How a pipeline threads them together
Ingestion lands raw events in the lake first, immutable and partitioned by arrival time, regardless of downstream shape, because that is the cheapest point to keep full fidelity and reprocess later if logic changes. A transformation stage then cleans, conforms, and models that data into the warehouse (or a warehouse-modeled zone), which is what BI and reporting query directly. The lake stays the archive and reprocessing source; the warehouse stays the fast, governed read path. Ad-hoc exploration of raw signals goes to the lake; repeated, latency-sensitive dashboard queries go to the warehouse.
The lakehouse alternative for regulated, audit-heavy settings
In a regulated environment where every read must be auditable and data cannot be duplicated across stores without tight control, a lakehouse pattern collapses the two zones into one: transactional table formats built directly on lake storage add warehouse-like properties (atomicity, consistency, isolation, durability, plus versioning and schema enforcement) to the lake itself, so there is a single lineage instead of two copies to reconcile for an audit. The trade is fewer warehouse-specific conveniences (workload isolation, some query-engine optimizations) in exchange for one authoritative copy.
Worked example
Consider a pipeline handling clickstream and change-data-capture (CDC) feeds from an operational database. Both land in the lake first as immutable, partitioned files, preserving full history even though the operational source only keeps a live snapshot. A daily transform job conforms and models that data into warehouse tables for a daily-active-user dashboard. If a support ticket needs a raw event from three weeks ago that never made it into any warehouse table, the lake still has it; the warehouse never needed to carry that raw volume at all. In a regulated variant of the same pipeline, the raw and modeled tables would instead live as transactional tables on the same lake storage, so the audit trail for "who read what, when" does not have to be reconciled across two separate systems.
Trade-offs & pitfalls
- Treating the lake as if it were queryable at BI speed is a common mistake; a lake needs a compute engine layered on top, and querying raw files directly for dashboards gets slow and expensive fast.
- Skipping the lake and loading straight into the warehouse loses reprocessing ability, forcing a re-pull from the original source if transformation logic ever needs to change.
- Warehouse compute and storage coupling can get expensive at high ingest volume; many pipelines route only aggregated, curated data to the warehouse and leave raw exploration to the lake.
- A lakehouse is not a universal upgrade. It earns its keep specifically when audit simplicity or a hard constraint against duplicating regulated data outweighs the workload-isolation and tuning conveniences a separate warehouse provides.
Unlock Full Question Bank
Get access to all 31 Data Pipeline Architecture and Design interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.