Skip to content

The Snapshot Paradox

When developers discover Event Sourcing, one of the first concerns that arises is replay performance. "What if a subject accumulates thousands of events? Won't rebuilding state become painfully slow?" The answer that usually follows is snapshots. Store the current state periodically, and start replaying from there instead of from the beginning. Problem solved, right?

Not quite. In our experience, snapshots are one of the most overrated concepts in the Event Sourcing toolbox. They solve a problem that rarely exists, and when they seem necessary, they often point to a deeper issue that snapshots merely paper over. That's the paradox: the feature designed to optimize performance frequently masks a modeling mistake that, if fixed, would make the optimization unnecessary.

The Performance Fear That Rarely Materializes

Let's start with the assumption behind the snapshot discussion: replaying events is slow. This sounds reasonable, but it rarely holds up in practice.

Modern hardware is remarkably fast at sequential reads. Replaying a few hundred or even a few thousand events takes milliseconds. The events themselves are small, typically a few hundred bytes each. Reading them sequentially from disk is one of the fastest operations a computer can perform. There is no random access, no complex joins, no index lookups. Just a linear scan through a compact stream of data.

We've seen this confirmed in production. In 18 Months of Events Fit on Four Floppy Disks, we shared real numbers from an internal application: 18 months of daily use produced just 8,610 events. The entire history fit into 5 MB. Replaying that takes barely any time at all.

Most business applications follow similar patterns. An order might generate five to ten events over its lifetime. A user account might accumulate a few dozen. Even busy aggregates in active domains rarely exceed a few hundred events. The performance bottleneck people imagine simply doesn't materialize for the vast majority of use cases.

So before you start thinking about snapshots, look at your actual numbers. Profile your replays. Measure how long they really take. You might find that the problem you're trying to solve doesn't exist yet, and may never exist.

When Big Subjects Signal Bad Modeling

But what about those cases where a subject does have tens of thousands of events? Or even hundreds of thousands? Surely snapshots are the answer then?

Before reaching for snapshots, it's worth asking a different question: why does this subject have so many events in the first place?

In our experience, a subject with an extraordinary number of events is often a symptom of a modeling problem. The aggregate is too broad. It's trying to be a container for everything related to a concept, rather than a focused consistency boundary for making decisions. We explored this pattern in detail in Your Aggregate Is Not a Table, and the lesson applies directly here.

Consider a bank account. If you model the entire account as a single aggregate, every transaction becomes an event on that aggregate. A busy account might process dozens of transactions per day. Over years, that adds up to hundreds of thousands of events. Snapshots seem like the obvious solution.

But think about what the aggregate actually needs to decide. Does it need the full transaction history to validate a withdrawal? Usually not. It needs the current balance and perhaps some daily limits. The real question is whether the aggregate boundary is drawn correctly, not whether the replay needs to be optimized.

What if, instead of one monolithic account aggregate, you modeled monthly ledgers? Each ledger covers a bounded time period, starts with a balance carried forward, and accumulates only the transactions for that month. Now each subject has a manageable number of events. The problem dissolves without introducing any additional complexity. You've just modeled the domain more accurately. Banks have thought in ledger periods for centuries, after all.

This is also related to what we discussed in DDD: Back to Basics: the fundamental triangle of Commands, Events, and State. If you understand that state is derived from events to support decisions, you naturally end up with smaller, more focused aggregates. And smaller aggregates mean fewer events per subject, which means less pressure to optimize replays in the first place.

When You Actually Need Snapshots

We're not saying snapshots are never useful. There are legitimate cases where they make sense. But those cases are rarer than most people assume, and they look different from what you might expect.

The strongest case for snapshots is when a subject genuinely accumulates a large number of events and the modeling is correct. This can happen in domains where the aggregate boundary is inherently broad: a long-running process that spans years, a complex configuration that evolves through hundreds of fine-grained adjustments, or a system that tracks high-frequency measurements over extended periods.

The key insight is to look for a natural business reason to take a snapshot. Don't just snapshot because a counter hit some arbitrary threshold like "every 100 events." Instead, ask whether the domain itself has a concept that corresponds to "closing the books" at a certain point.

The bank account example works well here too, but from a different angle. An annual account statement is a natural snapshot. It's not a technical optimization dressed up as a domain concept. It's a real business event that says: "As of December 31st, 2025, the balance was 42,000 EUR, and here is the complete summary." Starting the replay from this point isn't a hack. It reflects how the business actually thinks about its data.

This also means that there can be more than one type of snapshot for the same subject. A bank account might have monthly summaries, quarterly reports, and annual statements. Each captures a different level of detail for a different purpose. A snapshot for regulatory compliance might include different fields than a snapshot for quick balance lookups. Both are valid, and both can coexist in the same event stream.

This is why EventSourcingDB lets you define your own snapshot event names rather than imposing a single built-in mechanism. The snapshot is part of your domain, not a database feature.

Snapshots Are Events, Not Shortcuts

This brings us to one of the design decisions we're most convinced about in EventSourcingDB: snapshots are events. There is no separate snapshot storage, no special API, no dedicated mechanism. A snapshot is simply an event that your application writes to the subject's stream, just like any other event.

This might sound unusual at first, but it's a natural fit. Think about what a snapshot represents: a fact about the state of a subject at a specific point in time. "The account balance as of December 31st was 42,000 EUR." That's an event. It happened. It's a recorded fact. It belongs in the event stream alongside the transactions that led to it.

When you need to rebuild state, you read from the latest snapshot event forward. EventSourcingDB provides this through the fromLatestEvent option when reading events. You specify the event type of your snapshot, and the database returns that event along with everything that came after it. If no snapshot exists yet, it falls back to reading the entire stream. No special cases in your code, no separate storage layer, no synchronization headaches.

This approach keeps things simple. Your event stream remains the single source of truth. The snapshot is part of the history, not a separate artifact that might fall out of sync. And since the snapshot event has a name you choose, it naturally fits your domain language. An io.example.banking.annual-statement event tells you exactly what it is, both technically and conceptually.

State Is Always an Interpretation

There's a deeper reason why snapshots belong in your application rather than in the database, and it's important to understand it: state is always an interpretation.

When you replay events to build the current state of a subject, you're making decisions. Which events matter for this particular view? How do conflicting signals resolve? What do these events mean in the context of this specific use case? Different parts of your application might answer these questions differently.

We explored this idea in CQRS Without the Complexity: the same events feed different read models, each optimized for a different question. A catalog search, a borrower dashboard, and a librarian statistics panel all derive from the same event stream, yet each produces different state. A snapshot captures one particular interpretation of the event stream, not the interpretation.

This is why the database can't create snapshots for you. It doesn't know which interpretation you need. It doesn't know which fields to include, which events to consider, or how to handle edge cases in your domain logic. Only your application has that context. Only your application knows what "the current state" means for a given use case.

This also explains why you might need different snapshot types for the same subject. A snapshot for aggregate validation might contain a minimal set of decision-relevant fields. A snapshot for reporting might contain aggregated totals and trends. A snapshot for compliance might include a detailed audit summary. Each is a valid interpretation of the same events, frozen at a specific point in time. And each lives in the event stream as a first-class citizen.

Understanding this also helps you see why the full event history remains valuable even when snapshots exist. As we discussed in The Three-Cent Problem, there are situations where you need to go back to the original events to reconcile, audit, or debug. Snapshots optimize the common path, but they don't replace the history. They complement it.

Rethink Before You Snapshot

The snapshot paradox comes down to this: when you think you need snapshots, you usually don't. And when you actually do, they work best when they emerge from your domain rather than from a technical optimization strategy.

Before adding snapshots to your system, walk through these questions. Is the performance actually a problem, or just a theoretical concern? Could the large event count indicate a modeling issue worth fixing? Is there a natural business concept that corresponds to "closing the books"? And if you do create snapshots, are you treating them as domain events rather than as database shortcuts?

Event Sourcing works because it captures the full story. Snapshots don't replace that story. They add chapters of their own: moments where the narrative pauses, takes stock, and summarizes where things stand before moving on. When viewed this way, snapshots aren't a workaround for a limitation. They're another way to express what matters in your domain.

If you want to learn more about how EventSourcingDB handles snapshots, our documentation on snapshots explains the concepts and the best practices for snapshots and performance help you decide when and how to use them. And if you'd like to discuss whether snapshots are the right fit for your use case, or whether your domain model might benefit from a different approach, reach out to us at hello@thenativeweb.io. We'd love to think this through with you.