RecallDeck
Interview track

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.

312 cards15 topics
See plans and start trial

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 cards
Data Modeling

ETL/ELT & Pipelines

11 cards
ETL & Pipelines

Spark & Distributed Processing

10 cards
Spark

Streaming & Kafka

10 cards
Streaming

Storage & File Formats

9 cards
Storage & Formats

SQL & Query Optimization

10 cards
SQL Optimization

Data Quality & Orchestration

9 cards
Data Quality

Data Architecture & System Design

8 cards
Architecture

Python

122 cards
Core LanguageData Model & InternalsConcurrency & AsyncStdlib, Typing & Testing

Databases

42 cards
SQL Fundamentals

DevOps & Infra

35 cards
Docker, CI/CD & Linux

Behavioral

35 cards
Behavioral

Sample 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?

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:

  1. Workload profile — OLTP: many small operations (INSERT/UPDATE by primary key); OLAP: few heavy queries with GROUP BY and aggregations.
  2. Storage model — row storage reads the whole row (good for transactions); columnar reads only the needed columns and compresses better (good for analytics).
  3. Normalization — OLTP is normalized (3NF) for write integrity; OLAP is denormalized for read speed.
  4. 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?

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:

  1. 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.
  2. 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.
  3. 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?

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:

  1. 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.
  2. Cluster manager — negotiates resources: how many executors, how many cores and how much memory each. It does no computation itself.
  3. Executors — JVM processes on the workers. They run tasks, cache partitions, and serve data during shuffles. They live for the application's lifetime.
  4. Work hierarchyJob (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?

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:

  1. 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.
  2. Ordering is guaranteed only within a partition. More partitions → more parallelism, but no global ordering across the topic.
  3. Relationship: consumers ≤ partitions for full utilization. With more consumers than partitions, the surplus stays idle.
  4. 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?

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:

  1. 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.
  2. 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.
  3. Predicate pushdown — using stored per-block min/max statistics, a filter like WHERE date = ... skips whole blocks without reading them.
  4. 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?

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:

  1. Reading direction — from the innermost nodes (data access) up to the root (final result). In EXPLAIN ANALYZE compare the estimate (rows=) with reality (actual rows): a large gap exposes stale optimizer statistics.
  2. 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.
  3. Join method — Nested Loop, Hash Join, Merge Join (see table).
  4. 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.

Other interview tracks

RecallDeckSpaced-repetition interview prep