System Design
Batch vs Stream Processing
I run a pipeline at Netflix that takes in more than 8 billion events a day. The single decision that shapes a data system like that is whether you process in batches or as a stream. Here's how I think about it - and how I learned the difference the expensive way.
There is a number on a dashboard I watch most days: events per second flowing into the viewing pipeline I help run at Netflix. On a normal evening, when a few hundred million people settle in to watch something, that number climbs into the millions per second, and over a day it adds up to more than 8 billion events. Every play, pause, seek, and stop. Every quality switch. Every "are you still watching?"
Early in my career I would have built a system to handle that data exactly the wrong way - and I did, more than once, before I understood the distinction that governs everything in data processing: do you process data in batches, or as a continuous stream? It sounds like an implementation detail. It is actually the decision that determines what your system can and cannot do. This is how I think about it now, after years of getting it wrong and then getting it right at a scale that doesn't forgive mistakes.
The two ways to process data
There are two fundamental approaches to processing data, and almost every data system is built on one or a combination of both.
Batch processing collects data over a period of time and then processes it all together, in one large chunk, on a schedule. You accumulate a day's worth (or an hour's, or a week's) of data, then run a job that processes the whole pile at once. Think of it like doing laundry: you let it pile up, then wash a full load.
Stream processing processes data continuously, one event at a time (or in tiny micro-batches), as it arrives. There's no waiting to accumulate a pile - each event is handled the moment it shows up. Think of it like washing each dish the instant you're done using it, instead of letting them stack in the sink.
graph TD
subgraph BATCH[Batch Processing]
B1[Data accumulates over hours] --> B2[Scheduled job runs] --> B3[Process the whole pile at once] --> B4[Results, later]
end
subgraph STREAM[Stream Processing]
S1[Event arrives] --> S2[Process immediately] --> S3[Result, now]
S1b[Next event arrives] --> S2
end
This diagram contrasts the two approaches. In batch processing, data accumulates over hours, then a scheduled job runs and processes the whole pile at once, producing results later. In stream processing, each event is processed immediately as it arrives, producing a result right away, continuously, event after event.
The difference isn't just timing - it's a different shape of system, with different strengths, costs, and failure modes. Let me take each one honestly.
Batch processing: powerful, patient, and cheaper
Batch is the older approach, and for a long time it was the only approach. You schedule a job - say, every night at 2 a.m. - and it churns through everything that accumulated since the last run. The nightly job that computes "how many hours of content were watched yesterday, by region" is a batch job. It doesn't need yesterday's number until this morning, so there's no reason to compute it continuously.
What batch is genuinely good at:
- Throughput. When you process a huge pile all at once, you can be extremely efficient - read data in bulk, optimize the whole computation, use resources fully. For sheer volume of data processed per dollar, batch is hard to beat.
- Complex computations over complete data. Some questions can only be answered when you have all the data - "rank every title by total watch time this month." Batch naturally has the complete dataset in front of it.
- Simplicity and cost. A batch job runs, finishes, and releases its resources. It's often simpler to build, easier to reason about, and cheaper to run than a system that must be alive every second.
The price you pay for all that is latency. With batch, results are always stale by design - as old as the gap since the last run. If your job runs nightly, your answers can be up to a day old. For "yesterday's total watch hours," that's completely fine. For "is this account being used fraudulently right now," it's useless. A whole day of fraud would happen before the nightly job even noticed.
Important: Batch processing's defining trade-off: high throughput and low cost, but high latency. Results are only as fresh as your last scheduled run. This is perfect when you don't need up-to-the-second answers - reporting, analytics, billing, training data prep - and a poor fit when freshness is the whole point.
Stream processing: immediate, alive, and harder
Stream processing flips the model. Instead of waiting, you process each event as it arrives, so results are continuously up to date. The system is always on, always consuming the incoming flow.
The early mistake I made - more than once - was assuming that because batch was simpler and cheaper, I should default to it and only reach for streaming when forced. That's backwards in a world that increasingly expects things to happen now. When a viewer hits play and the system needs to update "continue watching" instantly, when a security system needs to flag a suspicious login as it happens, when a dashboard needs to show what's happening this second - batch simply cannot do it. The latency is structural; no amount of tuning a nightly job makes it real-time.
What stream processing gives you:
- Low latency. Results are available within milliseconds or seconds of an event happening. The system reflects reality now, not at the last scheduled run.
- Continuous insight. You see trends as they develop rather than discovering them after the fact - a traffic spike, a failing service, a fraud pattern - while you can still react.
- Smooth, constant load. Instead of a massive resource spike when a batch job kicks off, the work is spread continuously, which can be gentler on infrastructure.
And what it costs you:
- Complexity. A streaming system must be alive 24/7, handle events arriving out of order, deal with late or duplicate data, and recover from failures without losing or double-counting events. This is genuinely harder than a batch job that runs and stops.
- Handling "time" is subtle. When you process continuously, questions like "how many events in the last minute?" require careful handling of windows and event-time versus processing-time - a category of problem batch mostly sidesteps because it has all the data at once.
- Cost of always-on. A system that never sleeps consumes resources continuously.
The hardest lesson I learned at scale is that streaming's difficulty isn't in processing one event - that's easy. It's in processing billions of them correctly when some arrive late, some arrive twice, and the machine doing the processing occasionally dies mid-stream. Getting "exactly-once" correctness in a stream is one of the real challenges of data engineering, and it's why streaming systems like the ones built on Apache Kafka and Apache Flink exist - they solve those hard problems so you don't have to rebuild them.
How I actually choose
After all the mistakes, here's the decision that matters: how fresh does the answer need to be?
If the answer can be hours or a day old without anyone caring, use batch. It's simpler, cheaper, and higher-throughput, and you should not pay the complexity tax of streaming for data nobody needs in real time. Reporting, analytics, billing runs, generating training datasets, periodic aggregations - batch all the way.
If staleness defeats the purpose - if a delayed answer is a wrong answer - use streaming. Fraud detection, real-time dashboards, live recommendations, monitoring and alerting, anything a user experiences as "instant." The complexity is worth it because there's no alternative that meets the requirement.
| Batch | Stream | |
|---|---|---|
| Processes | Accumulated data, on a schedule | Each event, as it arrives |
| Latency | High (hours/days) | Low (ms/seconds) |
| Throughput | Very high | High, but per-event overhead |
| Complexity | Lower | Higher |
| Cost | Lower (runs and stops) | Higher (always on) |
| Best for | Reporting, analytics, billing | Real-time: fraud, dashboards, alerts |
A concrete scenario: the same data, two jobs
Let me make this concrete with the kind of decision I actually face. Take a single stream of viewing events. Two different questions need answering from it:
Question one: "Update this user's 'Continue Watching' row the moment they stop a show." This has to be instant - a viewer who stops mid-episode and opens the app on another device expects to see it right there. A delay of even a few minutes feels broken. This is a freshness requirement, so it goes to the streaming path: the stop event is processed the moment it arrives, and the user's state updates within seconds.
Question two: "How many total hours of this title were watched last week, by country?" This needs to be complete and accurate, but nobody needs it in real time - it feeds business reporting reviewed periodically. This is a completeness requirement, so it goes to the batch path: a scheduled job aggregates the full week's events at once, efficiently, and produces precise totals.
Same events, two completely different processing decisions, driven entirely by what each answer needs to be. This is the daily reality of data engineering at scale, and it's why the freshness-versus-completeness question is the one I keep coming back to.
Micro-batching: the middle ground
There's a useful middle option worth knowing: micro-batching. Instead of processing one event at a time (pure streaming) or a huge daily pile (pure batch), micro-batching processes small batches very frequently - say, every few seconds. It's a hybrid that gets most of streaming's freshness with some of batch's efficiency, by grouping a few seconds of events and processing them as a tiny batch.
This blurs the line between batch and stream usefully. Some popular processing frameworks are built around micro-batching precisely because it's a practical compromise: near-real-time latency (a few seconds, not milliseconds) with the throughput benefits of processing in groups. When you don't need millisecond freshness but a daily batch is far too stale, micro-batching often hits the sweet spot. It's a reminder that batch and stream aren't a strict binary - there's a spectrum, and micro-batching lives in the middle.
A mistake I see constantly
The most common mistake I see - and made myself - is reaching for streaming because it sounds more advanced, when the requirement doesn't actually need it. Streaming is harder to build, harder to operate, and more expensive to run. If the answer can be a few hours old without anyone caring, building a complex always-on streaming system for it is wasted effort and ongoing operational burden. The discipline is to start from the freshness requirement and earn your way into streaming, not to default to it because it's the impressive choice. Equally, the opposite mistake - forcing batch onto a genuinely real-time need and then bolting on awkward workarounds - is just as costly.
Both mistakes come from the same root: choosing the processing model by its reputation rather than by the requirement. Anchor every decision to the question's actual freshness need, and you'll reach for the right tool far more often.
The honest answer: you usually need both
Here's what nobody told me early on, and what I'll tell you: most serious systems don't choose. They use both, for different jobs.
At the scale I work at, the same viewing events feed two paths. A streaming path processes them in real time - updating "continue watching," powering live operational dashboards, feeding real-time personalization - because those need to be immediate. And a batch path processes the same events in nightly aggregations for analytics, business reporting, and training the models that drive recommendations - because those need completeness and efficiency, not immediacy. The streaming layer answers "what's happening now," the batch layer answers "what happened, completely and precisely." (This combined pattern is well known in data engineering, and there are named architectures for blending the two - but the core idea is simply: use streaming where freshness matters, batch where completeness and cost matter, over the same underlying event data.)
So the real skill isn't picking a side. It's recognizing, for each question your system needs to answer, whether that question needs an immediate answer or a complete one - and routing it to the right kind of processing. Get that mapping right and a system handling billions of events a day stays both fast where it must be and economical where it can be.
Interview: In a data-heavy system design, showing you understand this distinction is a strong signal. Frame it by freshness: "The fraud check needs to be real-time, so that's stream processing; the daily revenue report can be batch, since it just needs to be complete and is fine being a few hours old." And the senior move is recognizing you'll often want both over the same data - a streaming path for immediate needs and a batch path for complete, efficient aggregation. That's how real large-scale data systems are actually built.
A word on named architectures
The pattern of running streaming and batch paths over the same data has names you'll encounter. The Lambda architecture runs both a batch layer (for complete, accurate results) and a speed/streaming layer (for fresh, approximate results), then merges them - giving you accuracy and freshness at the cost of maintaining two code paths. The Kappa architecture simplifies this by treating everything as a stream and reprocessing the stream when you need to recompute, avoiding the duplicate batch code. You don't need to memorize these, but knowing they exist signals you understand the core tension they address: how to get both the freshness of streaming and the completeness of batch without maintaining two entirely separate systems. The practical reality, whatever you call it, is the one I keep returning to - route each answer to the processing it needs, and find the least painful way to do both where you need both.
What I'd tell my earlier self
The version of me building data pipelines in Lagos and Amsterdam reached for batch by default and bolted on real-time features awkwardly when forced, because streaming felt intimidating. The version of me at Netflix knows the question is simpler and sharper than that: does this answer need to be fresh, or complete? Fresh means stream. Complete-and-cheap means batch. Most real systems need both, for different answers, over the same data.
That reframe - from "which technology" to "what does this answer need to be" - is the whole thing. Get it right and the architecture follows. We go deep on data processing and the systems that power it across System Design Fundamentals.
Go deeper
- Stream processing (Wikipedia) - the model and its core ideas.
- Apache Kafka documentation - the backbone of many real-world streaming pipelines.
- Apache Flink - a stream-processing engine built for exactly the hard problems described here.
- Designing Data-Intensive Applications - the definitive deep reference on batch and stream processing.