If you remember five things
The whole answer in one glance. Everything below defends these five calls.
- It is a freshness question. The real fork is never "batch or stream" in the abstract, it is "how fresh does this result need to be, and what will fresh cost me?"
- Batch ends, streaming never does. Batch is a loop over a finite dataset, so it can sort, join, and rerun freely. Streaming loops over an infinite dataset, so it decides on a window with incomplete data.
- Batch by default. Reach for batch and add streaming only where a real user feels the delay. Streaming is a tool for low latency on unbounded data, not a status symbol.
- Event time plus watermarks. Bucket by when the event happened, not when you saw it, and use a watermark to wait out stragglers. Getting this wrong is why streaming numbers quietly disagree with batch.
- Always keep the raw log. Retain raw events in a replayable log so you can reprocess after a bug. State must be bounded (TTL or window) so a job does not grow until it falls over.
1The core idea#
Two models, one difference that drives everything else.
- Batch: collect data over a window, then run one job over the whole pile. A factory that saves up orders all day and ships one full truck at midnight. You accept delay for efficiency: the truck is cheap per package because it is full.
- Stream: handle each record as it lands, usually within milliseconds to a few seconds. Same factory, but a courier leaves the moment any order arrives. More cost per package, but the customer gets it fast.
The mental model I give juniors: batch is a loop over a finite dataset that ends; streaming is a loop over an infinite dataset that never ends.
- A batch job has a clear start and finish, so it can sort, join, and recompute freely.
- A stream job never finishes, so it only ever sees a window of recent data and decides with incomplete information.
The decision is almost never "batch or stream" in the abstract. It is "how fresh does this specific result need to be, and what will fresh cost me?" A fraud score that lands 6 hours late is useless. A monthly revenue report that is 200ms fresh is pointless over-engineering. Find the freshness budget first and the model usually picks itself.
2How it works#
A batch pipeline is simple to reason about.
- Data lands in storage (S3 or a data lake).
- A scheduler like Airflow kicks off a job every hour or every night.
- The job reads a bounded slice, crunches it, writes results somewhere queryable.
- Crash recovery is a rerun: the input is immutable and finite, so the second run produces byte-for-byte the same output as the first. That rerunnability is a quiet superpower.
A streaming pipeline looks different.
- Events flow into a durable log like Kafka or Kinesis.
- A framework (Flink, Spark Structured Streaming, Kafka Streams) keeps a long-running job that pulls events continuously, updates state, and emits results as it goes.
- There is no "rerun the input file" button, because the input never stopped arriving.
Two concepts make or break a streaming design, so know them cold.
Windowing. Since the stream never ends, you group events into windows.
- Tumbling: fixed buckets, say 5 minutes each. "Count logins per 5 minutes."
- Sliding: last 5 minutes, recomputed every minute, so windows overlap.
- Session: grouped by gaps in activity.
- Property juniors forget: in a tumbling window each event lands in exactly one bucket, so counts across buckets sum to the total. Sliding windows overlap, so they do not.
Event time vs processing time. An event born at 10:00:00 might reach your processor at 10:00:08 over a flaky mobile network.
- Processing time: bucket by when you saw it. Simple, but wrong near boundaries.
- Event time: bucket by when it happened. Correct, but you must wait for stragglers.
- The wait is a watermark: a marker that says "I am now fairly sure I have seen all events up to 10:00." Late data after the watermark gets dropped or sent to a side output.
- Getting this wrong is the single most common reason streaming numbers quietly disagree with batch numbers.
Reveal the answer
3Trade-offs#
This is where interviews are won or lost. Each model gives you something and takes something away.
| Dimension | Batch | Stream |
|---|---|---|
| Latency | Minutes to hours | Milliseconds to seconds |
| Throughput per dollar | High, big sequential reads | Lower, per-event overhead |
| Correctness | Easy, full dataset, reruns | Hard, partial view, late data |
| Operational burden | Low, jobs start and stop | High, always-on, stateful |
| Reprocessing | Trivial, rerun the job | Painful, replay the log carefully |
| Failure recovery | Rerun from input | Checkpoints and state restore |
| Mental overhead | Low | Steep |
What you give up with batch: freshness.
- An hourly window means users see an hour-old world.
- Resource use is spiky. A nightly job might slam 500 cores for 20 minutes then idle, which is wasteful or hard to schedule.
What you give up with streaming: simplicity and, often, sleep.
- The job runs forever, so it accumulates state that must be checkpointed, recovered, and bounded so it does not grow without limit.
- Exactly-once is achievable (Flink plus transactional sinks) but costs latency and complexity.
- Debugging is harder because you cannot just "look at the input file."
- A bug fix over last month's data means replaying millions of events, carefully, without double-counting.
Batch
- Latency
- Minutes to hours, tied to the window.
- Correctness
- Easy: full dataset, deterministic reruns.
- Best when
- Result is consumed on a human or daily rhythm.
- Breaks when
- A real user acts on the result within seconds.
Stream
- Latency
- Milliseconds to seconds.
- Correctness
- Hard: partial view, late and out-of-order data.
- Best when
- Low latency on unbounded data is the actual requirement.
- Breaks when
- You need exact historical numbers and have to recompute.
Reach for batch by default and add streaming only where a real user feels the delay. Streaming is not more "advanced." It is a tool for one problem: low latency on unbounded data. Teams that go streaming-first because it sounds impressive usually rebuild half of it in batch the day they need correct historical numbers.
4A worked example with real numbers#
Make the trade-off concrete, because the cost gap surprises people. Say we run at 50,000 events per second (4.32 billion events per day) and want a per-item count (think "views per video").
The batch option.
- Land raw events in S3, run an hourly Spark job.
- One hour is 180 million events, about 30 GB at 170 bytes each.
- A sequential scan plus aggregation on a modest cluster (say 20 cores) finishes in about 4 to 6 minutes, run 24 times a day.
- Cores idle the rest of the hour, so on spot instances this costs on the order of a few dollars a day, and the numbers are exact because every event in the window is present.
- The cost to the user: the dashboard shows a world that is on average 30 minutes stale, and up to 60 minutes stale right before the next run.
The streaming option.
- Run a Flink job with tumbling 1-minute windows on event time.
- It holds live state for every active key. With 2 million active videos that is a keyed counter per video plus checkpoint overhead, call it 1 to 3 GB of state checkpointed to durable storage every 30 seconds.
- The job never stops, so you pay for it 24/7, not 2 hours a day, plus the operational tax of a stateful always-on system.
- In exchange, the count is fresh to within about 90 seconds (one window plus the watermark wait).
So who consumes it decides the answer.
- Creator checking stats over coffee: the 20x buys nothing. Pick batch.
- Auto-scaler reacting to a launch-day spike: 30-minute-old numbers would scale up half an hour after the spike already melted the servers. Streaming is the only sane choice.
- Put the numbers on the whiteboard and the answer usually picks itself.
5Where it is used#
Batch wins when correctness beats speed and the result is consumed on a human or daily rhythm.
- Billing and invoicing. You want the exact total, computed once, reconcilable. Nobody bills in real time.
- Analytics and BI dashboards refreshed hourly or daily.
- ML training on a frozen snapshot of the last 90 days, not a live firehose.
- Data warehouse loads, dedup, and heavy joins across huge tables.
Streaming wins when a human or system acts on the result within seconds.
- Fraud detection. A declined card must resolve before the payment completes, so you have maybe 200ms.
- Real-time ops dashboards: active users right now, orders per second during a sale.
- Alerting and anomaly detection. An error-rate spike should page someone in seconds, not at tomorrow's batch run.
- Personalization that reacts to what you clicked 10 seconds ago.
In practice, big systems run both, and the patterns have names.
- Lambda architecture: a batch layer for accurate historical truth plus a speed (streaming) layer for fresh-but-approximate results, merged at query time. It works, but it is a maintenance tax because you write the same logic twice.
- Kappa architecture: skip the separate batch layer, treat everything as a stream over a replayable log, and recompute by replaying from the start. Cleaner when your streaming framework is strong enough to do your "batch" work as a bounded replay.
If an interviewer asks you to design Uber surge pricing or a trending-topics feed, this both-layers answer is often exactly what they are fishing for.
6Common pitfalls and gotchas#
Each one has failed a real candidate (or a real on-call).
- Treating micro-batch as true streaming. Spark Structured Streaming historically processed tiny batches every few seconds. Fine for "near real time," but if someone needs sub-100ms per event, micro-batch will not get there. Know your latency floor before you promise one.
- Ignoring late and out-of-order data. The classic. Streaming counts read 3% low because events after the watermark got dropped. Decide the policy on purpose: drop, correct, or route to a side output for reconciliation.
- Unbounded state. A counter per user kept for all time eventually runs out of memory. State needs a TTL or a window that expires old keys. I have seen a "simple" dedup job quietly grow to hundreds of GB and fall over during a traffic spike.
- Exactly-once that is not. People say "exactly-once" and mean "at-least-once plus hope." True exactly-once needs checkpointing plus idempotent or transactional sink writes. If your sink cannot dedupe, design for at-least-once and make the downstream effect idempotent.
- No replay plan. You shipped a metric bug for a week. Can you recompute? If raw events are not retained in a durable, replayable log, the answer is no, and that data is gone. Retain raw input. Always.
- Over-streaming. A Flink cluster powering a dashboard a human checks twice a day is a pager you carry for no reason. Match the model to the consumption rhythm.
Reaching for streaming because it sounds modern, with no latency target, no windowing, no late-data plan, and tool names standing in for reasoning. When the interviewer asks "what happens to an event that arrives 8 seconds late?" and you stall, the whole answer unravels. State the freshness budget first, then pick the model.
7How it shows up in interviews#
Interviewers rarely ask "explain batch vs stream" directly.
- They hand you a design ("count video views" or "detect fraudulent transactions").
- They watch whether you ask the right question first: how fresh does the result need to be, and who consumes it?
A weak answer: "I'll use Kafka and Spark for real-time processing."
- No latency target, no windowing, no late data.
- Tool names standing in for reasoning.
- The candidate picked streaming because it sounds modern, then stalls on "what happens to an 8-second-late event?"
A strong answer (the whole topic in four sentences):
Candidate: View counts for the creator dashboard can tolerate a few minutes of lag, so I would run a streaming job with tumbling 1-minute windows on event time, watermarked at 30 seconds for late mobile events, writing to a serving store. But the lifetime view count that drives payouts needs to be exact, so I would run a nightly batch job over the raw event log as the source of truth and reconcile any drift. Raw events stay in Kafka with 7-day retention so I can replay after bugs.
Why it lands. Every clause is a question the interviewer no longer has to ask.
- A latency budget: a few minutes.
- A windowing choice: tumbling 1-minute on event time.
- A late-data policy: watermark at 30 seconds.
- A correctness split: fresh dashboard vs exact payouts.
- A failure-and-replay plan: 7-day Kafka retention.
"What happens to an event that arrives 8 seconds after its window closed?"
"Your streaming count disagrees with the nightly batch count. Which is right and why?"
"How do you reprocess after shipping a metric bug that ran for a week?"
"Your streaming job is slowly eating memory. What is wrong?"
"You said exactly-once. Prove it."
What you can now explain in the room
- Why the real fork is a freshness budget, not "batch or stream" in the abstract.
- Why batch reruns deterministically while streaming must checkpoint and bound its state.
- Event time vs processing time, and why a watermark decides who gets dropped.
- The real cost gap: 20x fresher for a full day of compute plus a pager, from concrete numbers.
- When Lambda and Kappa each earn their keep, and the maintenance tax of writing logic twice.
- How to answer the design prompt: latency budget, windowing, late-data policy, correctness split, replay plan.
Want to see this applied end to end? Try the System Design Question walkthrough on designing a real-time analytics pipeline, where freshness, windowing, and a reconciling batch layer all have to coexist.
Get a prep plan built around your Batch vs Stream Processing interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.