CMD Guide
HomeSystem DesignDistributed File System

Batch Processing vs Stream Processing

Two Answers to "When Does the Data Get Processed?"

Batch processing and stream processing are two different answers to the same question: when should a system act on data it receives? Batch processing waits, collects a bounded amount of data, and processes it as one unit. Stream processing acts on each event (or a very small buffer of events) as it arrives, treating the data as an unbounded, never-ending sequence. The rest of this page works through the mechanics of each approach, a worked example of a subtle correctness bug in naive stream processing, and — because knowing the mechanics is not the same as knowing when to reach for which — an explicit decision guide for batch vs. stream and for the two most common architectures that try to get both at once: Lambda and Kappa.

diagram
diagram

Batch Processing

Definition

Batch processing operates on a bounded dataset: a finite collection of records gathered over some window of time (an hour, a day, a month) and processed as a single, complete unit. The system knows the full extent of the data before it starts — there is a first record and a last record.

Mechanics

Frameworks like Hadoop MapReduce and Apache Spark split the bounded dataset into partitions, run computation (map) independently per partition, then shuffle records by key across the cluster so that all records sharing a key land on the same reducer, and finally aggregate (reduce) per key. This shuffle-and-sort step is the main cost: it is I/O- and network-heavy, and it is why batch jobs measure completion time in minutes to hours rather than milliseconds. A job aggregating, say, 2 TB of clickstream data across roughly 200 executors might, for example, take on the order of 20 minutes end to end — the real number swings a lot with partition count, executor memory, and shuffle I/O, so treat it as a sense of scale, not a benchmark.

Characteristics

Use cases

Stream Processing

Definition

Stream processing operates on an unbounded dataset: events keep arriving indefinitely, and the system has no concept of "the last record." Instead of waiting for a complete dataset, a stream processor reacts to each event (or a very short micro-batch of events) as it arrives.

Event time vs. processing time

The moment an event is generated (event time) and the moment the processing system receives it (processing time, or arrival time) are not the same thing, and the gap between them is not fixed. A mobile app can buffer events while offline and flush them minutes later; a network hop can retry and add latency; a partition rebalance can delay delivery. Any windowing logic that buckets events by processing time rather than event time is implicitly assuming that gap is always small enough not to matter — an assumption that network reality does not guarantee.

Characteristics

Use cases

diagram
diagram

Worked Example: Why Naive Windowing Gets This Wrong

Suppose a stream processor wants to compute a count of events per minute, and — naively — buckets each event by the minute in which it arrives (processing time) rather than the minute in which it occurred (event time). Here is a trace of five events crossing the 12:00–12:01 boundary:

EventEvent timeArrival timeCorrect bucket (by event time)Naive bucket (by arrival time)Mis-bucketed?
e112:00:0512:00:0812:00–12:0112:00–12:01No
e212:00:4012:00:4512:00–12:0112:00–12:01No
e312:00:1512:00:3512:00–12:0112:00–12:01No
e412:00:5812:01:0212:00–12:0112:01–12:02Yes
e512:00:5012:01:1512:00–12:0112:01–12:02Yes

Only e4 and e5 actually land in the wrong bucket here: both occurred before 12:01:00 but arrived after it, so naive processing-time windowing counts them in the 12:01–12:02 bucket instead of 12:00–12:01. e1 and e2 arrive quickly enough that they never leave their event-time minute. e3 has a 20-second delay — larger than e1's or e2's — but it still arrives at 12:00:35, comfortably inside the same 12:00–12:01 minute as its event time, so it is not mis-bucketed despite the delay.

That last point is the trap: e3 being correct in this trace is a coincidence of how large the delay happened to be relative to where the event fell inside its minute, not evidence that naive processing-time windowing is safe in general. A 20-second delay is harmless for an event at 12:00:15; the same 20-second delay would be fatal for an event at 12:00:50 — which is exactly what happened to e5, with an even larger 25-second delay. Network delay is not a constant, and nothing in a naive processing-time window bounds it, so the naive version is wrong as a design even in a trace where only two of five events happen to be affected by it.

Watermarks: The Fix

A watermark is the stream engine's heuristic assertion that "no more events with event time earlier than T will arrive." It lets a window close and emit a result based on event time while still tolerating some out-of-order arrival — the engine waits until the watermark passes the end of a window before finalizing it, buying time for stragglers like e4 and e5 without waiting forever. Events that still arrive after the watermark has passed their window (very late data) are handled explicitly: dropped and counted as late, routed to a side output for separate handling, or merged into the window via an "allowed lateness" grace period, depending on the engine and the correctness the use case needs.

Engines differ in how they physically achieve this. Apache Flink is a native streaming engine: each record flows through the operator graph individually, and Flink achieves exactly-once state consistency by injecting checkpoint barriers into the stream — an asynchronous, Chandy-Lamport-style snapshot — so the pipeline never has to pause to take a consistent checkpoint. For a lean pipeline with small per-key state, a short checkpoint interval, and no expensive fan-out joins, that design can push end-to-end p99 latency down to, say, the tens of milliseconds. That figure moves a great deal with state size, checkpoint interval, network conditions, and hardware, so treat it as an illustration of what's achievable, not a number to write an SLA against — benchmark your own pipeline before committing to one. Spark Structured Streaming instead runs micro-batches: it buffers events for a short trigger interval (commonly around one second) and then executes a small batch job over the buffer using Spark's ordinary batch engine, which reuses batch's fault-tolerance and is simpler to reason about, at the cost of at least one trigger interval of added latency.

Windowing strategies

Lambda Architecture

Lambda architecture runs two pipelines over the same raw data: a batch layer that periodically recomputes a complete, accurate view over all historical data, and a speed layer (a stream processor) that computes a fast, approximate view over only the most recent data not yet covered by the last batch run. A serving layer merges the two at query time — approximate-but-current, corrected a few hours later by the accurate-but-delayed batch result.

Trade-off

You get both a low-latency approximate answer and an eventually-correct exact answer, but you pay for it with two codebases implementing conceptually the same transformation logic in two different frameworks. Every change to the business logic has to be made twice and kept in sync, and any drift between the batch and speed implementations shows up as a real discrepancy in the merged result — a whole category of bug that a single-pipeline design doesn't have.

Kappa Architecture

Kappa architecture drops the batch layer and treats everything as a stream. The log itself — a Kafka topic with retention long enough to hold the history you care about — is the single source of truth. There is one pipeline, written once in the stream-processing framework. Reprocessing historical data (a schema change, a bug fix in the transformation logic) is done by replaying the log from an earlier offset through that same pipeline, rather than maintaining a second batch pipeline to do it.

Trade-off

One codebase means no logic drift between "the fast path" and "the correct path" — there's only one path. The cost shows up when you need to reprocess a large amount of history: replaying weeks or months of a high-volume topic through a stream engine to backfill a fix can be slow and resource-intensive compared to a batch engine that is purpose-built for bulk, one-shot transforms over data already at rest. Kappa also leans on your log's retention and compaction capabilities being sufficient to hold the history you might need to replay — that's a storage and cost commitment, not just a processing one.

Choosing: The Decision Layer

Batch vs. stream

Lambda vs. Kappa — and when to use neither

How watermarks are generated in practice

A watermark is not magic; an engine must compute it from observable event timestamps. The common heuristic is bounded out-of-orderness: track the largest event timestamp seen so far, then publish watermark = max_event_time_seen - allowed_lateness. If the largest timestamp observed is 12:01:20 and you configured allowed_lateness = 30s, the watermark is 12:00:50; windows ending at or before 12:00:50 may close, while newer windows stay open for stragglers.

The other model is a punctuated watermark: the source embeds explicit progress markers in the stream, such as "partition 7 is complete through offset X / event time 12:01:00." This is stronger when the producer can know completeness (for example, sorted input, CDC boundaries, or file ingestion), but it requires source cooperation and fails if producers omit or delay the punctuation.

Watermark delay trade-off

Sources

🤖 Don't fully get this? Learn it with Claude

Stuck on Batch Processing vs Stream Processing? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Batch Processing vs Stream Processing** (System Design) and want to truly understand it. Explain Batch Processing vs Stream Processing from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Batch Processing vs Stream Processing** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Batch Processing vs Stream Processing** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Batch Processing vs Stream Processing** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes