Trade-offs · Production decisions
Event sourcing vs storing the current state
When to store every change as an event and when to store only the current state, with the numbers for storage, replay and rebuilds.
Asked at: SDE-2Senior
The decision
Most systems store the current state: one row per thing, and an update overwrites the old value. Event sourcing stores every change as an event, which is a fact in the past tense such as "MoneyAdded". Events go into an append-only store, where nothing is edited or deleted. The current state is found by applying the events in order. You are choosing between "simple and small" and "the full history is the truth".
Choose event sourcing when
- The history is the product, as in a money ledger, where someone must explain how a balance became what it is.
- A regulator or a dispute team asks "what was the state on this date" more than a few times a year.
- You must rebuild state or read models from facts, for example after a bug or when a new screen needs a new shape.
- Many writers change the same thing, and you want to reject a clash with an expected event number instead of a lock.
- The team has run an event-driven system before and can own versions, snapshots and rebuilds.
Choose current state when
- Data is mostly read and rarely changed, such as a profile, a catalogue or settings.
- You need history only for audit. A history table written in the same transaction gives you that without replay.
- Personal data must be deleted on request. Events never change, so deletion needs extra design.
- The product is young. Event shapes are hard to change later, and early shapes are usually wrong.
- You need to ask "which orders are still cooking" all day. A current-state table answers that with one query.
The numbers that change the answer
All numbers are assumptions for a wallet app. It has 50,000 wallets and 1,000,000 balance changes per day. An event is 200 bytes, a state row is 500 bytes, and a rebuild runs at 20,000 events per second.
| Question | Calculation | Result |
|---|---|---|
| Events per day | 1,000,000 x 200 bytes | 200 MB |
| Events per year | 200 MB x 365 | 73,000 MB = 73 GB |
| Current state | 50,000 x 500 bytes | 25 MB, fixed |
| Rebuild all events | 365,000,000 / 20,000 per second | 18,250 seconds = 5.1 hours |
Read the table like this. The state stays at 25 MB, and the events grow by 73 GB a year. A history table also grows by about 73 GB a year, so history costs the same in both designs. The difference is which copy is the truth.
Replay. Assumption: applying one event in memory takes 0.1 ms. A busy wallet with 100,000 events needs 100,000 x 0.1 ms = 10,000 ms = 10 seconds. A snapshot is a saved copy of the state after event number N. With a snapshot every 1,000 events, a load needs at most 1,000 x 0.1 ms = 100 ms.
Event shapes. Assumption: the event format changes 3 times a year. After 2 years the code reads 1 + 6 = 7 versions. Each old version needs a translator, called an upcaster. Current state also changes shape, but a database change rewrites the data once. Events keep every old shape.
Stream length. Take a snapshot when a stream passes 1,000 events (Assumption: our own limit).
What breaks with each choice
Event sourcing.
- Bad events live forever. A bug writes wrong events. Fixing the code does not fix them. You add a correcting event, or an upcaster that repairs them on read.
- No query on current state. You cannot ask an event store for all wallets below Rs 100. A read model must answer that, so you also run a projector, with its lag.
- Personal data. Events never change, so a deletion request is hard. Keep personal data outside the events and put only an ID in them. Or encrypt it with one key for each person and delete the key.
- Slow rebuilds. 5.1 hours with 20,000 events per second. Plan for it before you need it.
Current state.
- History is gone. After an overwrite you cannot say how the balance came to be. Fix: a history table in the same transaction, or a log of changes read from the database.
- Bugs hide. A wrong value is just a value. Nobody can replay to find when it went wrong.
What it costs
All numbers here are assumptions.
- Money: the event store grows by 73 GB a year in the example, plus a second store for read models. Disk is cheap. Backups, rebuild hours and engineer time are not.
- People: a team new to the idea needs about two months to get versions, snapshots and rebuilds right.
- Time to change your mind: Microsoft's guide says it is costly to move to or from event sourcing. Moving one part (the money ledger) from state to events takes weeks. Moving back means building the state from the events once and keeping a new history table.
How each level answers
The question is: "Design the wallet of a payments app. Do you store balances or events?"
SDE-1: Current state stores the latest balance. Event sourcing stores every change and calculates the balance from them.
SDE-2: For money I keep an append-only list of entries, because I must explain every balance. For the user profile I keep one row, because history adds nothing there.
Senior: 1,000,000 changes a day is only 73 GB a year, so size is not the problem. The costs are event versions, a 10 second replay for a busy wallet (100 ms with snapshots), and a 5 hour rebuild. For a bad event I add a correcting event, and I keep personal data outside the events.
Staff: I start with a history table in the same transaction, and move only the ledger to events when disputes justify the cost. The payments team owns the event formats and a nightly job that rebuilds balances from events and compares them with stored balances. Any difference stops the release and starts an investigation.
Say it in the interview
For the ledger I would store events so every balance is explainable, with a snapshot every 1,000 events to keep a load near 100 ms. The cost is event versions, no direct query on current state and a rebuild of about 5 hours. For profiles I would keep one row, and for audit alone I would use a history table in the same transaction.
Common mistakes
- Calling an audit log event sourcing. An audit log is a side record and the tables still hold the truth. In event sourcing the events are the truth.
- Storing state in the events. "Balance is now 42" tells you nothing about why. Store the intent, such as "two seats reserved".
- Using it for the whole system. Profiles and settings gain nothing from replay. Use it where history pays.
- Skipping snapshots and a rebuild plan. A stream of 100,000 events takes 10 seconds to load without a snapshot.
Sources
- Martin Fowler, "Event Sourcing" (all changes stored as events; state rebuilt by replay; snapshots): https://martinfowler.com/eaaDev/EventSourcing.html (opens in a new tab) (checked 26 Oct 2026).
- Microsoft Azure Architecture Center, "Event Sourcing pattern" (costly to move to or from; event versions; snapshots; personal data; not for simple systems): https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing (opens in a new tab) (checked 26 Oct 2026).
- The wallet numbers, event sizes, speeds and team times are assumptions made for teaching. The other numbers are calculations shown above.
Understand the trade-off. Practise explaining your choice.
In an AI mock interview, the interviewer can push on a choice like this one: it changes a number and asks if your answer still holds.