Section C · The Big Trade-offs

Batch vs Streaming

The central architectural fork of the interview — and the one candidates most often get wrong by reaching for streaming when a nightly batch meets the SLA at a fraction of the cost.

Batch, micro-batch, streaming

Before you can defend a choice, define the options crisply. The distinction is not "fast vs slow" — it's what triggers computation and what the unit of work is.

ModeUnit of workTriggerTypical latency
BatchA bounded dataset (a partition, a day, a full table)A schedule or an upstream dependency completingMinutes to hours
Micro-batchSmall bounded chunks carved from a streamA short timer (e.g. every 10s–1min) or a size thresholdSeconds to a couple of minutes
StreamingIndividual events (or tiny buffers), unboundedArrival of the event itselfMilliseconds to seconds

Micro-batch is the pragmatic middle: Spark Structured Streaming's default execution model chops an unbounded stream into a series of tiny batch jobs. It buys most of the freshness of streaming with the operational familiarity and throughput of batch, which is why it's the workhorse for a huge share of "near-real-time" pipelines. True record-at-a-time streaming (Flink) is what you reach for when single-digit-second or sub-second latency and rich per-key state actually matter.

The reframe

Batch and streaming are not two products; they're two points on a latency/cost curve. Most platforms run both — a streaming path for the handful of use cases with a tight freshness SLA, and batch for everything else. The interview question is rarely "which one" for the whole platform; it's "which one for this data path, and why."

What actually drives the choice

The decision is not about technology preference. It's driven, in order, by:

  1. Freshness SLA. The single dominant input. If a consumer needs data within seconds (fraud scoring, live ops dashboards, alerting), streaming earns its keep. If "by 6am" or "within the hour" is fine (finance reporting, most BI, ML training sets), batch or micro-batch wins comfortably. Ask what the consumer actually does with fresher data — often the answer reveals the real SLA is looser than the ask.
  2. Cost. Streaming means always-on compute and standing infrastructure (a Kafka/Flink cluster running 24/7) versus batch compute you spin up, run, and tear down. At modest volume a nightly warehouse job can be an order of magnitude cheaper than an equivalent streaming stack.
  3. Complexity & operational burden. Streaming pulls in watermarks, windowing, state stores, checkpointing, backpressure, and 24/7 on-call. Batch failures are re-runnable in the morning; a stalled stream is a page.
  4. Correctness under late/out-of-order data. Batch sees a whole bounded window at once, so late data is "just there." Streaming must decide when a window is done in the face of events that haven't arrived yet — a genuinely harder correctness problem (below).
  5. Team skill. A team fluent in SQL and dbt will ship a reliable batch platform far faster than they'll operate Flink well. Match the architecture to who has to run it at 3am.
The clarifying question that wins

"What decision or action does a consumer take on this data, and how stale can it be before that action is wrong?" This converts a vague "real-time" ask into a number, and the number decides the architecture. Fraud blocking needs seconds; a daily revenue dashboard read at 9am does not care whether the data landed at 2am or 5am.

Time, windows & correctness

The hardest part of streaming is that events arrive late and out of order, so you must distinguish two clocks:

  • Event time — when the event actually happened (stamped at the source). What you almost always want to aggregate by.
  • Processing time — when your engine saw it. Easy, but wrong for anything time-windowed, because a mobile client that was offline for an hour will inject "old" events into "now."

A watermark is the engine's assertion that "I don't expect any more events older than time T," which is what lets a windowed aggregation close and emit. Set it too tight and you drop late events; too loose and you hold state and delay output. This is the crux of streaming correctness — and it has a lot of depth.

Cross-link, don't re-teach

Event-time semantics, watermarks, allowed-lateness, and how to reprocess when late data lands are covered in depth in the companion guide: Production Data Pipelines · Late-Arriving Data. In this interview, know the concepts well enough to say "streaming forces me to make a watermark trade-off between latency and completeness that batch sidesteps" — then point to where the mechanics live rather than whiteboarding a full watermark diagram. Stay at the decision altitude.

The architectural takeaway: batch's correctness is easy because it operates on a closed window; streaming trades that ease for freshness, and pays in watermark tuning and late-data handling.

Exactly-once & reprocessing

"Exactly-once" is a property of the whole path, not a checkbox. In practice you get it by combining at-least-once delivery with idempotent or transactional sinks, plus checkpointed operator state.

  • Flink achieves end-to-end exactly-once via distributed checkpointing (Chandy–Lamport barriers) plus a two-phase-commit sink connector — output is committed only when a checkpoint completes.
  • Spark Structured Streaming gives exactly-once with replayable sources (Kafka offsets), a write-ahead log, and idempotent sinks (e.g. a Delta table with transactional commits).
  • Kafka offers transactions and idempotent producers, giving exactly-once within the Kafka-to-Kafka boundary; the sink still has to hold up its end.

Reprocessing is where the two paradigms diverge most sharply, and it's a favorite deep-dive:

  • Batch reprocessing is trivial — a bug in yesterday's logic means re-running yesterday's partition. Idempotent overwrite of a partition is the whole story.
  • Streaming reprocessing is a design problem. To fix a bug you must replay history from a retained log (Kafka retention or a lake replay), rebuild state, and reconcile with already-emitted results. This is precisely the pain Kappa architecture tries to make routine and Lambda tries to sidestep.
Interview trap

Claiming "exactly-once" without naming the mechanism (checkpointing + transactional/idempotent sink) reads as cargo-culting. And remember: exactly-once processing does not mean exactly-once side effects — if your job calls an external API, that call can still fire twice on replay.

Lambda vs Kappa

Two reference architectures for combining freshness with correctness:

  • Lambda runs two paths: a speed layer (streaming, approximate, low-latency) and a batch layer (slow, complete, authoritative) that periodically recomputes and corrects the speed layer's output. The serving layer merges them. Cost: you maintain the same business logic twice, in two codebases, and reconcile them.
  • Kappa keeps one path — streaming only. Reprocessing is done by replaying the log through the same code with new logic. Simpler conceptually and no duplicated logic, but it demands durable, replayable logs and an engine that can reprocess history fast.
Where the industry actually landed

Lambda's "two codebases" tax pushed the field toward Kappa-style, streaming-first designs — and, increasingly, toward unified engines where the same code runs on bounded and unbounded data (Flink's unified batch/stream, Beam's model, Spark's batch + Structured Streaming). The modern warehouse-native answer is often quieter still: land data incrementally and let a materialized view or dbt incremental model keep the result fresh. Naming this trajectory — "Lambda is largely legacy; I'd default to a unified or incremental approach unless there's a reason not to" — signals you've watched the space evolve.

The engine landscape

Match the engine to the mode and to the correctness/latency need. You should be able to place each of these on the board and say why.

EngineModelSweet spotWatch out for
Spark (batch)Bounded batchLarge-scale ETL, backfills, ML training setsNot for sub-minute latency
Spark Structured StreamingMicro-batch (+ continuous)Near-real-time on an existing Spark/Databricks stack; unified code with batchMicro-batch floor on latency (~seconds); heavy state can strain
FlinkTrue record-at-a-time streaming, statefulLow-latency, complex event-time windows, large keyed state, CEPOperational complexity; steeper team skill bar
BeamUnified batch/stream model, portableWrite once, run on Flink/Dataflow/Spark; Google Cloud DataflowAbstraction cost; runner-specific quirks
Warehouse-native streaming / materialized viewsContinuous ingest + incremental refreshSnowflake Snowpipe Streaming + Dynamic Tables, BigQuery streaming inserts + materialized viewsLatency floor higher than Flink; cost of continuous refresh
dbt incrementalBatch, incremental modelsAnalytics transforms on a schedule; SQL-first teamsNot streaming — freshness bounded by run cadence

A short Structured Streaming reader shows how thin the code difference between batch and micro-batch actually is — the same DataFrame API, a different source and a trigger:

stream_orders.py
# Micro-batch stream: read Kafka, aggregate by event-time window,
# tolerate 10 minutes of lateness, write exactly-once to Delta.
from pyspark.sql import functions as F

events = (spark.readStream
    .format("kafka")
    .option("kafka.bootstrap.servers", "broker:9092")
    .option("subscribe", "orders")
    .load())

agg = (events
    .select(F.from_json(F.col("value").cast("string"), schema).alias("e"))
    .select("e.*")
    .withWatermark("event_ts", "10 minutes")          # late-data tolerance
    .groupBy(F.window("event_ts", "5 minutes"), "region")
    .agg(F.sum("amount").alias("revenue")))

(agg.writeStream
    .format("delta")
    .outputMode("append")
    .option("checkpointLocation", "/chk/orders")       # state + exactly-once
    .trigger(processingTime="30 seconds")              # micro-batch cadence
    .start("/lake/gold/revenue_5m"))

The decision matrix

This is the artifact to reproduce on the whiteboard. Score the requirement against each axis; the column with the better fit for your dominant requirement wins that path.

AxisBatchMicro-batchStreaming
Latency / freshnessMinutes–hoursSeconds–minutesMilliseconds–seconds ✅
CostLowest — ephemeral compute ✅Moderate — near-continuous computeHighest — 24/7 standing infra
ComplexityLowest ✅ModerateHighest — state, watermarks, checkpoints
Correctness w/ late dataEasiest — closed windows ✅Manageable — watermarksHardest — watermark tuning
Operational burdenRe-run in the morning ✅Some 24/724/7 on-call, backpressure
ReprocessingTrivial — re-run a partition ✅ModerateHard — replay + state rebuild
Team skill requiredSQL / dbt ✅Spark familiarityFlink / streaming expertise

Notice batch wins six of seven axes; streaming wins the one that, when it matters, matters absolutely — latency. That asymmetry is the whole trade-off in a sentence.

When to choose what

Deliver these as defended defaults, then name the condition that flips them.

Choose batch (or micro-batch) when…
  • The freshness SLA is measured in minutes-to-hours (most BI, reporting, ML training sets).
  • Cost matters and volume is modest-to-large but not latency-sensitive.
  • The team lives in SQL/dbt and correctness/re-runnability matter more than seconds.
  • You need trivial reprocessing and backfills — a partition re-run.
  • Default here. Reach for micro-batch when "hourly" quietly becomes "within a few minutes."
Choose streaming when…
  • A consumer takes an automated action on the data within seconds — fraud blocking, real-time personalization, alerting, live operational dashboards.
  • The value of the data decays fast (a fraud signal at T+1 hour is worthless).
  • You need continuous, stateful computation (sessionization, running aggregates, CEP) that batch would recompute wastefully.
  • You already run the streaming infra and team skill for it — the marginal cost of one more stream is low.
The trap to name out loud

The most common senior-level miss is reaching for streaming when a nightly batch meets the SLA at a fraction of the cost and effort. If no consumer acts on the data within the batch window, streaming buys you nothing but a standing cluster, a harder correctness story, and a 24/7 pager. Say it explicitly in the room: "I'd default to batch here — nothing consumes this data faster than daily, so streaming would add cost and operational burden with no payoff. I'd move the fraud path, and only the fraud path, to streaming because it needs sub-second scoring." Splitting the platform by SLA rather than picking one mode for everything is the staff-level answer.

Storage sits underneath all of this — where the batch and streaming outputs land, and in what format, is the next fork. Continue to 06 · Lakes, Warehouses & Lakehouses.

✓ Check yourself

Answer from memory before opening each one.

  1. A mobile client was offline for an hour and then uploads its events. If you window by processing time, where do they land, and what does a watermark set too tight or too loose cost you?

    Show answer

    They land in the current window, not the hour in which they happened, so the aggregate is wrong. Window by event time. A watermark that is too tight drops late events. One that is too loose holds state longer and delays output. See time, windows and correctness.

  2. A stakeholder asks for real-time revenue. The only reader is a finance dashboard opened at 9 a.m. What do you ask, and what is the likely outcome?

    Show answer

    Ask what action is taken on the data and how stale it can be before the action is wrong. For a dashboard read at 9 a.m. the answer is hours, so a nightly or hourly batch meets it for far less cost and on-call load. See what drives the choice.

  3. A transform bug is found, and yesterday's numbers are wrong. Compare the fix in a batch pipeline and in a streaming pipeline.

    Show answer

    In batch you re-run yesterday's partition with an idempotent overwrite. In streaming you must replay history from a retained log, rebuild operator state, and reconcile with results already emitted, so retention has to cover the repair window. See exactly-once and reprocessing.

  4. A Flink job calls a payment provider for each record and is configured for exactly-once. After a restart some customers get two emails. Why?

    Show answer

    Exactly-once covers state and the sink commit, not side effects. A replay after a checkpoint re-runs the call to the provider. Send with an idempotency key, or move the effect behind a transactional or idempotent sink. See exactly-once and reprocessing.

  5. A teammate proposes Lambda: a streaming layer for speed and a batch layer to correct it, so the numbers are right in the end. What cost do you raise, and what do you recommend instead?

    Show answer

    The same business logic lives in two codebases that must agree, and the serving layer has to merge them. Prefer one path, either a streaming-first design that replays the log to reprocess, or incremental batch if the SLA allows. See Lambda vs Kappa.

  6. A platform has fraud blocking in seconds, daily finance reports, and weekly model training. Where does streaming go?

    Show answer

    Only on the fraud path, which acts on data within seconds and loses value as it ages. Batch the rest, since nothing consumes it faster than daily. Splitting by SLA keeps the standing cluster and pager to one path. See when to choose what.

Takeaway

  • Batch, micro-batch and streaming are points on a latency and cost curve. Most platforms run more than one, chosen per data path.
  • The freshness SLA decides first. Ask what action is taken on the data and how stale it can get before the action is wrong.
  • Streaming costs always-on compute, state, watermarks and on-call. Batch wins on cost, complexity, late data and reprocessing, and streaming wins only on latency.
  • Aggregate by event time. A watermark trades completeness against latency, and batch avoids that choice because its windows are closed.
  • Exactly-once is a property of the whole path: checkpointed state plus a transactional or idempotent sink. It does not cover external side effects.
  • Reprocessing is a re-run in batch and a replay with state rebuild in streaming.
  • Lambda duplicates logic. Default to one path, unified or incremental, unless a requirement forces two.

Whichever mode you pick, the output has to land somewhere, in some format. → Lakes, Warehouses & Lakehouses

Further reading