Skip to content
ExponaBLOG
APACHE SPARK & DATA ENGINEERING · DAY 077 / 100

Spark interview questions: practical tips and trade-offs

Questions for data engineering roles.

Expona Admin7 min read

If you're preparing for a data engineering interview focused on Apache Spark, you'll want to move past surface-level definitions and dig into the practical trade-offs that come with real production decisions. Spark interview questions: practical tips and trade-offs tend to focus less on memorized APIs and more on understanding why certain optimizations matter and when they backfire. We've seen teams struggle with shuffle bottlenecks, partition misconfigurations, and join strategies that looked good in development but melted under load—so let's walk through the patterns that actually show up in the hiring conversations.

What is a shuffle? — Data redistribution across executors for wide operations

A shuffle is one of the most expensive operations in Spark because it moves data across the network between executors. Whenever you perform a wide transformation—one that requires data from multiple partitions to be grouped together—Spark has to shuffle. Operations like groupByKey(), join(), repartition(), and distinct() all trigger shuffles.

To understand why this matters in an interview: shuffles involve serialization, disk I/O (spill to disk when memory is tight), network transfer, and deserialization on the receiving end. They're also where data skew can amplify into real performance cliffs. In a typical e-commerce pipeline we worked on, a shuffle on user IDs with a few power users generating 80% of events turned a 2-minute job into a 40-minute job.

Here's what a shuffle looks like in code:

python

# This triggers a shuffle
df.groupBy("user_id").agg(count("*")).show()

# So does this
df1.join(df2, "user_id")

# And this
df.repartition(200)

Interviewers typically ask this to see if you understand the performance implications and whether you know how to spot shuffle operations in a query plan. Check the execution plan with explain() to see where shuffles happen:

python

df.groupBy("user_id").agg(count("*")).explain()
# Look for "Exchange" in the output—that's your shuffle

The key insight: shuffles aren't always bad, but they're always expensive. The question is whether the business value of the operation justifies the cost.

Transformations vs actions? — Lazy plan building vs execution triggers

This is the foundation of Spark's lazy evaluation model. Transformations build a directed acyclic graph (DAG) of operations without running anything. Actions actually execute the DAG and return results to the driver or write to storage.

Transformations include map(), filter(), select(), groupBy(), and join(). Actions include show(), collect(), count(), write(), and take().

In an interview, this question tests whether you understand Spark's execution model and can explain why calling collect() on a large DataFrame will crash your driver. Let's look at a real scenario:

python

# All transformations—nothing runs yet
df = spark.read.parquet("events.parquet")
df = df.filter(col("event_type") == "purchase")
df = df.select("user_id", "amount")
df = df.groupBy("user_id").agg(sum("amount"))

# This action triggers execution
df.show()

# This also triggers execution (and would crash if the result doesn't fit in driver memory)
results = df.collect()

# This writes to storage (another action)
df.write.parquet("output/")

The practical implication: you can chain transformations without worrying about memory or compute, but once you call an action, Spark materializes the entire result. At a CRM platform we worked with, engineers were calling show() inside a loop on growing dataframes, not realizing each show() re-executed the entire DAG from scratch. Lazy evaluation only optimizes what actually runs—careless actions kill performance.

Interviewers also use this to gauge whether you'd write inefficient code like repeatedly reading the same file or computing the same grouping twice:

python

# Anti-pattern: compute the same thing twice
summary1 = df.groupBy("product_id").agg(count("*")).collect()
summary2 = df.groupBy("product_id").agg(sum("price")).collect()

# Better: compute once and reuse
summary = df.groupBy("product_id").agg(
    count("*").alias("count"),
    sum("price").alias("total_price")
)
summary.collect()

How do you fix skew? — Salting, broadcast joins, or AQE

Data skew is when certain partition keys have vastly more data than others. One executor gets stuck processing 10GB while the others finish 100MB. We've seen this completely dominate interview technical rounds because it's where the theory of shuffle operations meets real-world firefighting.

There are three main approaches:

Salting (adding random noise to keys)

If your groupBy("user_id") has a few power users generating 80% of events, you artificially split their data across multiple partitions by adding a random suffix:

python

from pyspark.sql.functions import rand, concat, lit

# Add random salt to skewed keys
df_salted = df.withColumn(
    "user_id_salted",
    when(
        col("event_count") > 1000,  # heuristic for skewed key
        concat(col("user_id"), lit("_"), (rand() * 10).cast("int"))
    ).otherwise(col("user_id"))
)

# Now groupBy distributes the power user evenly
result = df_salted.groupBy("user_id_salted").agg(count("*"))

# If you need to aggregate back to original keys
final = result.withColumn(
    "user_id",
    substring_index(col("user_id_salted"), "_", 1)
).groupBy("user_id").agg(sum("count"))

The trade-off: you add an extra aggregation stage, but you convert a bottleneck into parallelizable work.

Broadcast joins

Instead of shuffling both sides of a join, broadcast the smaller table to all executors and perform a local join. This only works when one table is small enough to fit in memory on every executor:

python

from pyspark.sql.functions import broadcast

# Broadcast the smaller table (e.g., product lookup)
result = events.join(
    broadcast(products),
    "product_id"
)

Spark will automatically broadcast tables under 10MB by default, but you can force it manually or tune the threshold with spark.sql.autoBroadcastJoinThreshold.

Adaptive Query Execution (AQE)

In Spark 3.0+, AQE automatically detects skew and applies fixes at runtime. Enable it:

python

spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.adaptive.skewJoin.enabled", "true")

Interviewers respect answers that mention AQE because it shows you know current best practices. In production, we've seen AQE reduce 15-minute joins to 3 minutes by automatically salting skewed keys without code changes.

repartition vs coalesce? — Full shuffle vs merging partitions

repartition(n) redistributes data across n partitions using a full shuffle. coalesce(n) merges partitions locally without moving data between executors.

Use repartition when you want to change the number of partitions and are willing to shuffle. Use coalesce when you want to reduce partitions cheaply (common before writing small outputs):

python

# Full shuffle—expensive but distributes evenly
df.repartition(300)

# Cheap merge—combines adjacent partitions
df.coalesce(10)

Here's a real scenario from a ride-hailing pipeline: after filtering events down to 2% of original size, the DataFrame still had 500 partitions from the original read. Executor overhead and task scheduling were killing performance. Calling coalesce(50) merged partitions locally and cut job time by 40%.

But if you need to distribute data more evenly after a skewed filter, repartition is the right choice despite the shuffle:

python

# Skewed: some partitions might be empty or tiny
df_filtered = df.filter(col("country") == "US")

# Cheap but partitions may be imbalanced
df_filtered.coalesce(50)

# Expensive but ensures even distribution
df_filtered.repartition(50)

The interview answer: understand the trade-off and explain why you'd choose each. If you blindly use repartition, interviewers will ask about the shuffle cost. If you always use coalesce, they'll ask about handling data skew.

Common mistakes

  • Ignoring explain() output: Many candidates can describe shuffles theoretically but haven't actually read an execution plan. Always verify your understanding with df.explain().
  • Calling collect() on large results: Claiming you understand lazy evaluation and then immediately saying "I'd collect the final result to see what we got" breaks credibility. Collect only when the result genuinely fits in memory.
  • Over-optimizing premature skew solutions: Salting works but adds complexity. Try broadcast joins or AQE first. Interviewers reward pragmatism over premature cleverness.
  • Confusing repartition with a magic scaling fix: Adding more partitions doesn't automatically make Spark faster. It can actually slow things down by creating scheduling overhead. The right partition count depends on executor count, data size, and task duration.
  • Not mentioning monitoring: Talking about trade-offs without mentioning that you'd check Spark UI metrics, driver logs, and executor memory suggests you haven't debugged real performance issues.

Key takeaways

  • Shuffles redistribute data across the network—they're expensive, necessary for wide operations, and the source of most Spark performance issues. Understand what triggers them.
  • Lazy evaluation means transformations build a plan, actions execute it. Use this to avoid redundant computations and explain why collect() on 10GB crashes the driver.
  • Fix skew with salting (split hot keys), broadcast joins (avoid shuffle entirely), or AQE (automatic runtime fixes). Know the trade-offs of each.
  • repartition shuffles, coalesce merges locally. Choose based on whether you need even distribution or just want to reduce scheduling overhead.
  • Interviewers care about whether you've debugged real production issues, not textbook definitions. Mention Spark UI, execution plans, and metrics when discussing trade-offs.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles