Data Engineer interview prep
A spaced-repetition deck of 312+ Data Engineer interview questions — organised by topic and difficulty, and resurfaced right before you'd forget. Preview a few cards below, then choose access to study the whole track on an Anki-style SM-2 schedule.
7 days free on monthly or yearly · every feature included.
What's covered
Every topic in this track, grouped the way you'd study it.
Data Modeling & Warehousing
11 cardsETL/ELT & Pipelines
11 cardsSpark & Distributed Processing
10 cardsStreaming & Kafka
10 cardsStorage & File Formats
9 cardsSQL & Query Optimization
10 cardsData Quality & Orchestration
9 cardsData Architecture & System Design
8 cardsPython
122 cardsDatabases
42 cardsDevOps & Infra
35 cardsBehavioral
35 cardsSample questions
A few cards from the deck — reveal each answer, then choose access to study the full set on a schedule.
How does OLTP differ from OLAP, and why don't you run analytics on the production OLTP database?
How does OLTP differ from OLAP, and why don't you run analytics on the production OLTP database?
Short answer: OLTP serves short transactions — inserting and updating individual rows with low latency, while OLAP answers analytical queries that scan millions of rows. OLTP stores data row-wise, OLAP column-wise. You don't run heavy analytics on the production OLTP database because it competes for resources with transactions and tanks production latency.
In depth:
- Workload profile — OLTP: many small operations (
INSERT/UPDATEby primary key); OLAP: few heavy queries withGROUP BYand aggregations. - Storage model — row storage reads the whole row (good for transactions); columnar reads only the needed columns and compresses better (good for analytics).
- Normalization — OLTP is normalized (3NF) for write integrity; OLAP is denormalized for read speed.
- Resource isolation — an analytical scan floods the buffer cache and disk, so transactions start to wait.
| Criterion | OLTP | OLAP |
|---|---|---|
| Purpose | transactions | analytics |
| Operations | row reads/writes | column aggregations |
| Storage | row-wise | columnar |
| Normalization | 3NF | denormalized (star) |
| Metric | latency, TPS | scan throughput |
⚠️ Common mistake: running reports directly against the production database. Extract data into a separate warehouse (ETL/ELT) so analytics doesn't disturb transactions.
What is the difference between ETL and ELT, why did ELT win with cloud warehouses, and when does ETL still make sense?
What is the difference between ETL and ELT, why did ELT win with cloud warehouses, and when does ETL still make sense?
Short answer: ETL transforms data before loading (on a separate engine), while ELT first loads raw data into the warehouse and runs transformations inside it in SQL. ELT became the default because cloud MPP warehouses (Snowflake, BigQuery, Redshift) scale compute cheaply and separate storage from compute.
In depth:
- ETL — transform on an intermediate engine (historically pricey ETL servers). Only clean, ready data lands in the warehouse. Downside: logic is locked in the tool and the raw data is lost.
- ELT — load the raw layer as-is, transform via dbt/SQL. Upside: raw is preserved, transformations are version-controlled in Git, and the warehouse handles scaling.
- Why ELT won — separating storage/compute made in-warehouse compute cheap and elastic; analysts prefer keeping logic in SQL.
| Criterion | ETL | ELT |
|---|---|---|
| Where transform runs | On a separate engine | Inside the warehouse |
| Raw layer | Usually lost | Preserved |
| Scaling | Bound by the engine | Elastic (MPP) |
| Typical stack | Informatica, SSIS | Fivetran + dbt + Snowflake |
When ETL still fits: heavy preprocessing before the warehouse, masking PII/compliance (raw cannot be loaded), or a source easier to coerce to schema on the fly.
⚠️ Common mistake: thinking ELT means "no transformations." The transformations didn't disappear — they just moved into the warehouse and run after loading.
Describe Spark's architecture: driver, executors, cluster manager. How is a job broken into jobs, stages, and tasks?
Describe Spark's architecture: driver, executors, cluster manager. How is a job broken into jobs, stages, and tasks?
Short answer: The driver builds the plan and coordinates execution, the cluster manager (YARN, Kubernetes, Standalone) allocates resources, and executors on the workers run tasks and hold data in memory. Every action launches a job, which the scheduler cuts into stages at shuffle boundaries, and each stage into tasks by partition count.
In depth:
- Driver — the process running your code and the
SparkSession. It builds the logical and physical plan, the DAG of stages, schedules tasks, and collects results. If the driver dies, the whole application dies. - Cluster manager — negotiates resources: how many executors, how many cores and how much memory each. It does no computation itself.
- Executors — JVM processes on the workers. They run tasks, cache partitions, and serve data during shuffles. They live for the application's lifetime.
- Work hierarchy —
Job(one per action) →Stage(bounded by shuffles) →Task(one per partition, the smallest unit of parallelism).
┌──────────────┐
│ Driver │ plan + DAG + scheduler
│ SparkSession │
└──────┬───────┘
│ request resources
┌──────▼───────┐
│Cluster Manager│ YARN / K8s / Standalone
└──────┬───────┘
┌──────────┼──────────┐
┌────▼────┐ ┌───▼────┐ ┌───▼────┐
│ Executor│ │Executor│ │Executor│ tasks + cache
└─────────┘ └────────┘ └────────┘
⚠️ Common mistake: conflating executor cores with tasks — parallelism is capped at num executors × cores, and if there are fewer partitions than slots, some cores sit idle.
How do partitions and consumer groups provide parallelism, and how do partition count and consumer count relate?
How do partitions and consumer groups provide parallelism, and how do partition count and consumer count relate?
Short answer: The partition is the unit of parallelism: within a consumer group each partition is read by exactly one consumer. So effective parallelism is capped by the partition count — consumers beyond that sit idle.
In depth:
- Consumer group — a set of consumers sharing a
group.id, across which partitions are distributed (rebalance). Each partition is assigned to one consumer in the group. - Ordering is guaranteed only within a partition. More partitions → more parallelism, but no global ordering across the topic.
- Relationship: consumers ≤ partitions for full utilization. With more consumers than partitions, the surplus stays idle.
- Scaling: partitions set the ceiling. Their count is easy to increase but not decrease, and it changes how keys map to partitions.
| Partitions | Consumers | Result |
|---|---|---|
| 4 | 2 | 2 partitions per consumer |
| 4 | 4 | 1 partition each — max parallelism |
| 4 | 6 | 4 work, 2 sit idle |
⚠️ Common mistake: adding consumers to go faster while forgetting the ceiling is the partition count. Beyond it, consumers sit with no partitions assigned.
How does columnar storage differ from row storage, and why is columnar faster and cheaper for analytics?
How does columnar storage differ from row storage, and why is columnar faster and cheaper for analytics?
Short answer: In a row format the values of a single record sit together; in a columnar format all values of one column sit together. Analytical queries (aggregates over a few of dozens of columns) read only the needed columns, compress far better and support predicate pushdown — so less I/O and lower cost.
In depth:
- Column projection (projection pushdown) — a query needs 3 of 50 columns, and a columnar engine reads only those three off disk. A row format must read the whole row.
- Compression — a column holds homogeneous values (one type, similar data), so dictionary, run-length and delta encoding reach compression ratios several times higher than on a heterogeneous row.
- Predicate pushdown — using stored per-block min/max statistics, a filter like
WHERE date = ...skips whole blocks without reading them. - Row format wins for point operations: read/write a whole record (OLTP, one-record-at-a-time streaming).
Row layout: [id=1,name=A,ts=..][id=2,name=B,ts=..][id=3,...]
└── record 1 ──┘└── record 2 ──┘
Columnar layout:
id: [1, 2, 3, ...] ← read only the columns
name: [A, B, C, ...] you need, each one
ts: [.., .., .., ...] compressed separately
⚠️ Common mistake: treating columnar as universally better. For row-at-a-time writes and whole-record reads (streaming, transactional updates) a row format (Avro) is more efficient.
How do you read a query execution plan (EXPLAIN), and what do you look at first?
How do you read a query execution plan (EXPLAIN), and what do you look at first?
Short answer: You read a plan bottom-up and inside-out: the leaves are table access (Seq Scan / Index Scan), joins and aggregations sit above. Hunt for the most expensive node, full scans of large tables under a filter, and the chosen join method.
In depth:
- Reading direction — from the innermost nodes (data access) up to the root (final result). In
EXPLAIN ANALYZEcompare the estimate (rows=) with reality (actual rows): a large gap exposes stale optimizer statistics. - Access method — Seq Scan (full scan) vs Index / Index Only Scan. A full scan of a big table under a selective filter is the first candidate for tuning.
- Join method — Nested Loop, Hash Join, Merge Join (see table).
- The expensive node — the one with the largest
cost/time; that's where you focus.
| Method | When chosen | Cost |
|---|---|---|
| Nested Loop | small outer input + index on inner | cheap at small volumes, else O(N·M) |
| Hash Join | large unsorted sets, equi-join | builds a hash table in memory |
| Merge Join | both inputs sorted on the key | pays off when sorting already exists |
EXPLAIN ANALYZE
SELECT o.id, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= DATE '2026-01-01';
-- Hash Join (cost=... rows=...) <- join method
-- -> Seq Scan on orders o <- full scan: filter not on an index
-- Filter: (created_at >= ...)
-- -> Hash
-- -> Seq Scan on customers c
⚠️ Common mistake: looking only at the final cost and ignoring the rows vs actual rows gap — that gap is exactly what reveals stale statistics and a bad plan choice.
Ready to make it stick?
Start your first session in under a minute. Your future self, mid-interview, will thank you.
Questions about this track
How should I prepare for a Data Engineer interview?
Study the concepts you'll be asked to explain, not just the ones you can code. RecallDeck's Data Engineer track gives you 312+ curated interview questions and resurfaces each one with an Anki-style SM-2 schedule right before you'd forget it — so the answers are still there under pressure on interview day.
What topics does the Data Engineer track cover?
The Data Engineer track is organised into the core areas Data Engineer interviews actually test, grouped by topic and by difficulty (Concept, Junior, Middle, Senior). You can preview the full outline and sample questions above before signing in.
Is spaced repetition effective for Data Engineer interview prep?
Yes. Actively recalling an answer and grading yourself honestly builds far more durable memory than re-reading notes. RecallDeck schedules each Data Engineer card to reappear at the moment you're about to forget it, so your daily reviews shrink while your recall holds.
Can I try the Data Engineer track before paying?
Yes. Monthly and yearly access include a seven-day trial of the complete Data Engineer track, the full SM-2 scheduler, statistics, flexible pacing, and cram mode. You can cancel online before the first charge.