
Conquering the Data Engineering Interview: A Deep Dive into Classic Questions
Landing a data engineering role requires demonstrating a unique blend of software engineering fundamentals, deep SQL expertise, and distributed systems design. The interview process is designed to test all these facets.
In this comprehensive guide, we break down some of the most common and critical data engineer interview questions. We won't just give you the answers; we'll explain the why and how behind them, providing the depth you need to impress your interviewers.
1. The Algorithmic Foundation
Q1: Merge Overlapping Intervals
Question: You’re given a list of intervals (e.g., [[1,3], [2,6], [8,10], [15,18]]); how would you merge overlapping intervals? What’s the time and space complexity?
Answer:
This question tests your ability to work with data structures and design efficient algorithms.
Solution:
-
Sort: First, sort the list of intervals based on their start time. This is the key step that allows us to merge in a single pass.
-
Merge: Initialize a merged list with the first interval. Then, iterate through the sorted list:
-
If the current interval's start time is less than or equal to the end time of the last interval in the merged list, they overlap. Merge them by updating the end time of the last merged interval to be the
max(last_interval_end, current_interval_end). -
If there's no overlap, simply append the current interval to the merged list.
-

Python Code:
def merge(intervals):
if not intervals:
return []
# Sort by the start time
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
for current_start, current_end in intervals[1:]:
last_merged_start, last_merged_end = merged[-1]
if current_start <= last_merged_end:
# Overlapping intervals, update the end
merged[-1] = [last_merged_start, max(last_merged_end, current_end)]
else:
# No overlap, add the interval
merged.append([current_start, current_end])
return merged
# Example
intervals = [[1,3], [2,6], [8,10], [15,18]]
print(merge(intervals)) # Output: [[1, 6], [8, 10], [15, 18]]
Complexity:
-
Time Complexity: O(n log n), dominated by the sorting step. The subsequent linear scan is O(n).
-
Space Complexity: O(n) (or O(1) if we ignore the output storage) to store the merged list.
Q2: Deduplicating Millions of Records
Question: Design an efficient algorithm to deduplicate millions of records in a stream or batch dataset. How would you handle memory constraints?
Answer:
This tests your knowledge of distributed computing and trade-offs.
Batch Processing (e.g., Spark):
-
Use a distributed framework like Apache Spark.
-
For exact deduplication, use
dropDuplicates()ordistinct()on the entire record or a subset of columns that define uniqueness. This involves a full-data shuffle but is straightforward. -
For memory constraints, Spark handles the data partitioning and processing across a cluster, so the memory burden is distributed.
Stream Processing:
-
Bloom Filters: This is a classic solution. A Bloom filter is a probabilistic, memory-efficient data structure that can tell you if an element definitely is not in a set or may be in a set.
-
How it works: For each record, calculate a hash. Check the Bloom filter. If the hash is not present, it's definitely a new record—emit it and add the hash to the filter. If the hash may be present, you might skip it (accepting a small false positive rate) or check a secondary store.
-
Handling Memory: Bloom filters are incredibly space-efficient, requiring only a few bits per element. You can tune the false positive rate based on available memory.
-
Handling Memory Constraints:
-
Key-Based Partitioning: Process data in chunks or partitions (e.g., by date, user id). Deduplicate within each partition first, then across partitions if necessary. This breaks the problem into smaller, memory-manageable pieces.
-
External Sort-Merge: If data is too large for memory, sort it externally on disk (using the deduplication key) and then do a linear scan to remove adjacent duplicates.
-
Approximate Deduplication: For use cases that allow it, use hyperloglog or other probabilistic structures to estimate unique counts without storing every unique identifier.
2. Mastering SQL and Window Functions
Q3: Find the 2nd Highest Salary per Department
Question: Write a SQL query to find the 2nd highest salary in each department using window functions.
Answer:
WITH ranked_salaries AS (
SELECT
department_id,
employee_id,
salary,
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) as rank_num
FROM
employees
)
SELECT
department_id,
employee_id,
salary
FROM
ranked_salaries
WHERE
rank_num = 2;
-
PARTITION BY department_id: Creates separate windows for each department. -
ORDER BY salary DESC: Orders salaries from highest to lowest within each department. -
DENSE_RANK(): Assigns a rank. Ties get the same rank, and the next rank number is not skipped. This is usually the intended behavior for "Nth highest" questions. UsingRANK()could result in no 2nd rank if there are ties for 1st.
Q4: ROW_NUMBER() vs. RANK() vs. DENSE_RANK()
Question: Explain the difference. When would you use each?
Answer:
| Function | Behavior on Tie | Subsequent Rank | Use Case |
|---|---|---|---|
ROW_NUMBER() |
Assigns arbitrary, unique numbers (e.g., 1, 2, 3). | Continues sequentially (4, 5, 6). | Pagination, selecting a distinct top-N per group. |
RANK() |
Assigns the same rank to ties (e.g., 1, 1, 3). | Skips the next rank(s) (next is 3). | Ranking competitions where you want to reflect gaps (e.g., Olympic medals). |
DENSE_RANK() |
Assigns the same rank to ties (e.g., 1, 1, 2). | Does not skip ranks (next is 2). | Finding "Nth highest" values where you don't want gaps. |
Q5: Calculating Running Totals
Question: Suppose you need to calculate running totals per user over time. How would you implement this? What performance pitfalls would you avoid?
Answer:
sql
SELECT
user_id,
transaction_date,
amount,
SUM(amount) OVER (
PARTITION BY user_id
ORDER BY transaction_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM
transactions;
-
PARTITION BY user_id: Resets the running total for each user. -
ORDER BY transaction_date: Defines the order of the summation. -
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: This is the default frame forSUMwithORDER BY, but it's good practice to be explicit. It defines the window as all rows from the start of the partition up to the current row.
Performance Pitfalls to Avoid:
-
Large, Unbounded Windows: On a large dataset, the window for a long-lived user could become huge, causing memory issues on a single node. If possible, define a bounded window (e.g.,
ROWS BETWEEN 30 PRECEDING AND CURRENT ROWfor a 30-day running total). -
Data Skew: If one user has 99% of the transactions (
user_id='super_user'), the node processing that partition will be overwhelmed. Mitigate by salting the key or ensuring your processing engine can handle skew. -
Lack of Partitioning: Without
PARTITION BY, the entire table is one window, which will be extremely slow and likely fail on large data.
3. Metrics, A/B Testing, and Product Sense
Q6 & Q7: Analyzing a Model Regression
Q6: After deploying a new recommendation model, CTR increases by 10% but CVR drops by 5%. How would you analyze this?
Q7: Which additional metrics would you track to decide whether to roll back the model?
Answer:
This tests your analytical rigor and understanding of business metrics.
Analysis Plan:
-
Validate the Data: First, rule out data quality issues. Was the tracking for clicks and conversions implemented correctly for the new model? Is the time period for comparison correct?
-
Segment the Data: Don't look at aggregate numbers alone.
-
By User Cohort: Are the gains in CTR coming from new users who are clicking more but not converting, while power users are dissatisfied? Or vice versa?
-
By Item/Content Type: Is the new model recommending "clickbaity" items that have high CTR but low conversion potential? Is it cannibalizing sales of high-value products?
-
By Traffic Source: Is the effect consistent across web, iOS, and Android?
-
-
Analyze Funnel Depth: Did we just move the problem? Perhaps users click more but drop off later in the funnel. Check metrics like "Add to Cart" rate and "Checkout Initiation" rate.
-
Statistical Significance: Ensure the observed changes are statistically significant and not just random noise.
Additional Metrics to Track:
-
Revenue Per User (RPU): The ultimate bottom-line metric. A 5% drop in CVR likely hurts revenue more than a 10% CTR gain helps it.
-
Long-Term Value (LTV): Are we sacrificing long-term user trust and retention for short-term engagement?
-
Category Diversity: Is the new model creating a "filter bubble" and reducing the diversity of recommendations?
-
Zero-Rate: The percentage of users who receive no recommendations at all. Did this change?
-
Time to Conversion: Did the customer journey become longer?

Q8: Designing an A/B Experiment
Question: How would you design an A/B experiment to balance CTR and CVR in recommendations?
Answer:
-
Hypothesis: "By blending engagement (CTR) and conversion (CVR) signals in our ranking algorithm (e.g.,
score = CTR^α * CVR^β), we can increase overall revenue without a significant loss in user engagement." -
Unit of Diversion:
user_id(to avoid intra-user contamination). -
Cohorts:
-
Control (A): The current model (optimized primarily for CTR).
-
Treatment (B): The new model with the blended ranking objective.
-
-
Primary Metric: Revenue Per User (RPU). This is the North Star that balances CTR and CVR.
-
Guardrail Metrics: CTR, CVR, User Retention (7-day), and overall site-wide revenue to ensure no negative ecosystem effects.
-
Duration: Run the experiment long enough to capture full user conversion cycles (e.g., 1-2 weeks).
-
Analysis: Use a t-test or similar to check for statistically significant lifts in RPU. If RPU is significantly higher for (B), roll it out.
4. Distributed Systems & Pipeline Design
Q9: Delta Lake Schema Enforcement vs. Evolution
Question: Explain Delta Lake’s schema enforcement vs. schema evolution. What are the risks of enabling schema evolution in production?
Answer:
-
Schema Enforcement (Validation): This is the default behavior. Delta Lake uses the write-ahead log (transaction log) to check that the schema of the DataFrame being written is exactly compatible with the schema of the target table. If you try to write a column that doesn't exist or has a wrong data type, the write fails. This ensures data quality and integrity.
-
Schema Evolution: This allows the schema to change automatically during a write. You enable it with
.option("mergeSchema", "true")in Spark.-
New Columns: Columns present in the DataFrame but not in the table are added to the end of the schema.
-
Nulls: For existing columns, if the DataFrame has a missing column, it is set to
null.
-
Risks of Schema Evolution in Production:
-
Unintended Schema Changes: A bug in your code could accidentally add garbage columns to your production table, polluting the schema.
-
Data Type Mismatches: It only handles additive changes. It will not automatically change the data type of an existing column (e.g.,
inttostring), which would still cause an error. -
Downstream Breakage: Consumers of the table (e.g., BI reports, ML models) might break if they are not built to handle a dynamically evolving schema.
Recommendation: Use schema evolution cautiously. A better practice is to use explicit ALTER TABLE commands in a controlled, CI/CD-driven pipeline for schema changes.
Q10: Designing a Fault-Tolerant Streaming Pipeline
Question: You’re asked to design a fault-tolerant, real-time streaming pipeline at scale. Which components would you choose and why?
Answer:
-
Ingestion (Source): Apache Kafka or Amazon Kinesis. Why? They are durable, distributed log-based messaging systems. They provide decoupling, buffering, and the ability to replay data in case of failures—the foundation for fault tolerance.
-
Processing (Compute): Apache Flink or Spark Structured Streaming. Why?
-
Flink is often preferred for very low-latency (<100ms) processing and its excellent state management. It provides true streaming semantics.
-
Spark Structured Streaming is excellent for micro-batch processing (latency ~100ms to a few seconds), leverages the massive Spark ecosystem, and is easier to find developers for.
-
-
Storage (Sink): Delta Lake on a cloud data lake (S3/ADLS/GCS). Why? It provides ACID transactions, schema enforcement, time travel (data versioning), and unified batch/streaming storage. It's a far more reliable and performant sink than plain cloud storage.
Why it's Fault-Tolerant:
-
Kafka/Kinesis retains data even if consumers are down.
-
Flink/Spark track processing progress through checkpointing (periodically saving state to durable storage) and offset tracking (committing which messages have been processed). On failure, they can restart from the last successful checkpoint.
-
Delta Lake ensures writes are atomic (transactional), so no partial results are visible.

Q11: Exactly-Once Semantics in Spark
Question: How would you ensure exactly-once semantics in a Spark Structured Streaming job?
Answer:
Exactly-once means each record is processed once and only once, even if failures occur. Spark achieves this through a combination of:
-
Fault-Tolerant Sources: Using a source like Kafka that can replay data and track offsets.
-
Checkpointing: Enabling checkpointing (
writeStream.option("checkpointLocation", "/path")) is mandatory. Spark writes its current progress (source offsets, intermediate state) to durable storage here. -
Idempotent Sinks: Using a sink that supports idempotent writes (e.g., Delta Lake) or transactional writes. When Spark restarts from a checkpoint, it will replay the exact same batch with the exact same data. If the sink can handle duplicate writes (e.g., with a transaction ID), it will ensure the final output is written only once.
The end-to-end guarantee is provided by the integration of these three components: replayable source + state checkpointing + idempotent sink.
Q12: Batch vs. Streaming for Ad-Clicks
Question: Compare batch vs. streaming pipelines for ad-click analysis. What trade-offs would you highlight to stakeholders?
Answer:
| Aspect | Batch Pipeline | Streaming Pipeline |
|---|---|---|
| Latency | High (Hours to days) | Low (Seconds to minutes) |
| Cost | Generally lower (leverages efficient bulk compute) | Generally higher (requires always-on resources) |
| Complexity | Lower (Well-understood, SQL-based tools) | Higher (State management, message delivery semantics) |
| Use Cases | Historical reporting, BI dashboards, ML training, long-term aggregations. | Real-time fraud detection, live dashboards, real-time personalization, alerting. |
| Handling Late Data | Easier (Can re-process entire batches) | More complex (Requires watermarks & allowed lateness) |
Trade-offs to Highlight:
-
"Do we need to know about a click in seconds to prevent fraud, or is knowing the daily trend tomorrow morning sufficient for your business goal?"
-
"Building a real-time system will cost ~2-3x more in development and cloud resources. Is the value of real-time data worth this premium?"
-
"A batch system is simpler and more robust for accurate historical reporting. A streaming system is better for immediate action."

5. The Behavioral Round
Q13: Learning from Failure
Question: Tell me about a time when you failed on a data project.
Answer Strategy (Use the STAR method):
-
Situation: Briefly describe the project. "I was building a pipeline to calculate customer LTV."
-
Task: What was your goal? "To deliver accurate daily LTV figures to the finance team."
-
Action: What did you do? "I designed and implemented the pipeline using..." Crucially, describe the mistake. "...but I made an assumption about the data source that was incorrect. I didn't fully validate that the
purchase_amountfield was net of refunds." -
Result: What was the impact? "This led to a 15% overestimation of LTV for two days before it was caught." Most importantly: What did you learn and how did you fix it? "I immediately halted the pipeline, corrected the logic to subtract refunds, and backfilled the data. I learned that I should never assume data quality. I implemented a new validation step in our CI/CD process where any new column used in a critical metric must have its definition and quality signed off by the source team."
Q14: Taking Ownership
Question: Describe a situation where you took ownership of a challenging ETL pipeline without being asked.
Answer Strategy (STAR method):
-
Situation: "Our main product analytics pipeline was chronically slow and failing 2-3 times a week, causing delays for the entire data team."
-
Task: "While it wasn't my direct responsibility, the bottlenecks were hurting everyone's productivity."
-
Action: "I took the initiative to profile the pipeline. I discovered the issue was data skew in a large join operation. I proposed and implemented a solution using salting techniques to distribute the load evenly. I coordinated the release during off-hours and created detailed documentation for the new design."
-
Result: "The pipeline's runtime decreased by 70%, and it hasn't failed in over six months. This improved the productivity of the entire data team and earned me recognition from my manager for demonstrating leadership."
Q15: ETL Pipeline Design
Question: Design an ETL pipeline to process 1M+ ad-click and impression records per day. Handle late-arriving data, deduplication, and schema changes.
Design:
-
Ingestion: Use a message queue like Kafka to ingest clicks/impressions in real-time. This decouples producers from consumers and handles backpressure.
-
Processing (Batch - Lambda Architecture): For this volume (low), a daily batch job (e.g., Spark on Airflow) is sufficient and simpler.
-
Deduplication: Read the day's data from Kafka (or from cloud storage where Kafka topics are archived). Use Spark's
dropDuplicates()on a unique event ID or a combination of fields (user_id,ad_id,timestamp). -
Late-Arriving Data: This is where partitioning is key. Don't partition your final table only by
event_date. Use a "processed date" partition (the day the data was ingested) and an "event date" partition. Your daily job should not just process yesterday's data, but also scan the lastNdays (e.g., 7 days) ofevent_datepartitions for any late-arriving records. This is called incremental processing or "lookback".
-
-
Storage: Write the processed data to a Delta Lake table partitioned by
event_dateandprocessed_date. -
Schema Changes: Do not use automatic schema evolution. All schema changes (adding a column) should be done through explicit
ALTER TABLEcommands as part of a controlled deployment process. Use Delta Lake's schema enforcement to catch errors early. -
Orchestration: Use Airflow to run the daily Spark job, managing dependencies and retries.

