Event SourcingCQRS
Event Sourcing in Practice: Lessons from 3 Years in Production
The promises were real. So were the sharp edges. A frank retrospective on building event-sourced systems.
Published February 10, 2026
Event sourcing is seductive in theory: an immutable log of everything that ever happened, time travel debugging, effortless audit trails. After three years running it in production, I can confirm — the promises are real.
The Snapshot Problem
What no blog post tells you: replaying 50,000 events to reconstruct state on every read is not acceptable in production. You need snapshots. Snapshots need versioning. Versioning needs discipline.
We settled on snapshotting every 1000 events, with a 24-hour TTL on the snapshot cache. This keeps cold-start replay under 200ms.
Code Reference
typescript
async function rebuildAggregate<T extends Aggregate>( id: string, repo: EventStore, SNAPSHOT_INTERVAL = 1000 ): Promise<T> { const snapshot = await repo.getSnapshot<T>(id); const fromVersion = snapshot?.version ?? 0; const events = await repo.getEvents(id, fromVersion); let state = snapshot?.state ?? initialState<T>(); for (const event of events) { state = applyEvent(state, event); } if (events.length >= SNAPSHOT_INTERVAL) { await repo.saveSnapshot(id, state, fromVersion + events.length); } return state; }
↑ Snapshotting an aggregate every N events