Snapshots vs Stories

A photograph freezes a moment. A diary records the journey. Both capture reality, but in different ways. The photograph shows where things are; the diary shows how they got there.

Systems face the same choice. Do we store snapshots (current state) or stories (event history)? Each approach has its wisdom.

The Snapshot Approach

Store the current state. Update it when things change.

yaml
    storage:
        current_game_state: { board: [...], turn: white, ... }
    
    on event:
        current_game_state = apply(current_game_state, event)
        save(current_game_state)           // overwrite the old

Advantages:

  • Fast reads: current state is immediately available
  • Simple queries: "where is the white king?" is trivial
  • Predictable storage: size doesn't grow with history

Disadvantages:

  • History is lost: we can't see past states
  • No undo: previous state is overwritten
  • No audit trail: we can't answer "what happened?"

The Story Approach

Store events. Derive current state when needed.

sql
    storage:
        events: [event_1, event_2, ..., event_n]
    
    on event:
        events.append(new_event)           // never delete, only append
    
    to get current state:
        replay(events)

Advantages:

  • Full history: every past state is recoverable
  • Natural undo: just replay fewer events
  • Audit trail: complete record of what happened and when
  • Debugging: can reproduce any historical scenario

Disadvantages:

  • Slow reads: must replay to get current state (unless cached)
  • Complex queries: "where is the white king?" requires replay
  • Growing storage: events accumulate forever

The Trade-Off Space

python
                    Snapshot              Story (Events)
    -----------------------------------------------------
    Read speed      Fast                  Slow (replay)
    Write speed     Medium (update)       Fast (append)
    Storage         Fixed                 Growing
    History         Lost                  Complete
    Undo            Hard                  Trivial
    Audit           None                  Built-in
    Queries         Easy                  Complex

Neither is universally better. The choice depends on:

  • How important is history to your domain?
  • How often do you read current state vs historical state?
  • What kinds of queries do you need?
  • How much storage can you afford?

The Hybrid: Events + Snapshots

Most production systems use both:

yaml
    storage:
        events: [all events, forever]
        snapshots:
            at_event_1000: state_at_1000
            at_event_2000: state_at_2000
            ...
        current_snapshot: latest_state     // for fast reads

Write path (fast):

    on event:
        events.append(event)
        current_snapshot = apply(current_snapshot, event)
        
        if event_count divisible by 1000:
            save_snapshot(current_snapshot, at: event_count)

Read path (fast):

sql
    current state:      return current_snapshot
    
    historical state:   find nearest snapshot before target
                        replay from there

This gives us:

  • Fast current-state access (from snapshot)
  • Full history (from events)
  • Reasonable historical access (from nearest snapshot + replay)

When to Choose What

Prefer snapshots when:

  • History genuinely doesn't matter
  • Queries on current state are complex and frequent
  • Storage is severely constrained
  • The domain is simple CRUD (create, read, update, delete)

Prefer events when:

Use both when:

  • History is valuable (audit, compliance, debugging)
  • Undo/redo is important
  • You need to answer "what happened?" questions
  • Multiple views of the same data exist (different read models)
  • The domain is complex with many business rules
  • You want event benefits but need fast current-state reads
  • History matters but so does query performance
  • You're building a serious production system

A Philosophical Note

The snapshot says: "This is what is."

The story says: "This is what happened."

The snapshot is a noun---a state of being. The story is a verb---a sequence of becoming. Both are valid ways to understand reality.

In our chess game:

A chess master studies both: the position to decide the next move, the history to understand how they got there.

  • The snapshot shows the position. A frozen moment.
  • The story shows the game. A living history.

So far, our history has been linear: one event after another, a single timeline. But what happens when we want to explore alternatives? When we ask "what if?" In the next chapter, we discover forking paths.