Hermes Wiki

Memento Pattern

Concept

Memento captures an object's internal state at a point in time so it can be restored later, without breaking that object's encapsulation to do it. The object being snapshotted (the "originator") produces an opaque memento — a value object holding just enough state to reconstruct the originator later — and hands it to a separate "caretaker" that stores it (often in a stack, for undo history) but cannot inspect or modify its contents. When restoration is needed, the caretaker hands the memento back to the originator, which is the only party that knows how to interpret it. The key discipline the pattern enforces is that no other part of the system ever reaches into the originator's private fields directly to save or restore state — only the originator itself does that, through the memento's narrow interface.

This shows up constantly in real systems even where nobody names it "Memento": a text editor's undo stack is a sequence of mementos, each one a snapshot of document state before an edit; a database's write-ahead log combined with checkpointing lets a crashed process restore to a known-good state; a game's save-file system snapshots player/world state at a point the player can return to; React's useState/useReducer history in a time-travel debugger is a stack of application-state mementos. Event sourcing is the pattern's logical extreme — instead of storing periodic full-state mementos, it stores every state-changing event and reconstructs state by replaying them, trading memento simplicity for a complete audit trail and the ability to reconstruct any past state, not just the ones explicitly checkpointed.

Tradeoffs

Approach Benefit Cost
Memento (originator produces opaque snapshots, caretaker stores them) Restoration doesn't break encapsulation — only the originator interprets its own saved state; caretaker logic (how many snapshots to keep, when to prune) is fully separate from state logic Snapshotting full state repeatedly can be memory-expensive for large objects; naive implementations keep unbounded history unless the caretaker explicitly prunes
Direct field copy by an external caller Simplest possible code for a small, stable object Breaks encapsulation — external code needs to know the originator's internal shape, and any internal refactor of the originator breaks every place that copies its fields
Event sourcing (store every state-changing event, replay to reconstruct) Full history of every past state, not just checkpointed ones; the event log itself is a complete audit trail Reconstructing current state requires replaying from the last snapshot (or from the beginning) — real systems still need periodic memento-style snapshots as an optimization to bound replay time

Memento and event sourcing aren't competitors so much as points on the same spectrum: a pure memento approach snapshots full state at chosen moments and discards the deltas that got you there, while event sourcing keeps every delta and treats state as a derived, replayable value. Most production event-sourced systems end up implementing memento-style snapshotting anyway, purely as a performance optimization so replay doesn't have to start from event zero.

When to use / when not to

  • Use when a system needs to restore a prior state — undo/redo stacks, checkpoint/rollback in long-running workflows, save points in stateful applications — and the object being restored has internal state that shouldn't be exposed to the code doing the saving/restoring.
  • Especially valuable when the originator's internal representation is expected to change over time; keeping snapshot logic inside the originator means external callers never need updating when internals shift.
  • Don't reach for a hand-rolled memento class when the object being saved is already a plain immutable value (a simple struct/record) — just keep copies of the value itself; the pattern's ceremony exists specifically to protect encapsulated state, and there's nothing to protect when the state was already a transparent value type.
  • Watch the caretaker's storage growth — an undo stack with no cap, or a checkpoint table nobody prunes, is a slow, unbounded-memory leak dressed up as a feature.

Common pitfall

Letting the "caretaker" (the code that stores snapshots — an undo manager, a checkpoint table) reach into a memento's contents to make decisions, rather than treating it strictly opaque. Once caretaker code starts branching on memento internals ("if this snapshot's status field is X, skip it"), the caretaker and originator are coupled through the memento's shape even though the pattern's whole purpose was avoiding exactly that coupling — the next internal refactor of the originator now has to account for a caretaker that was supposed to know nothing about it. The discipline that keeps the pattern paying off is treating the memento as a black box everywhere outside the originator, even when peeking feels convenient in the moment.

Engineering Lens

The pattern's real payoff surfaces during an incident retro: "can we roll this back, and to what granularity" is a question a system with proper memento/checkpoint discipline can answer cleanly, while a system that mutates state in place with no restore path can only answer with a forward-fix, often under worse time pressure than a rollback would have required. The broader lesson generalizes past undo buttons — any workflow with a meaningful failure mode benefits from an explicit "what does reverting to a known-good state look like" answer designed in advance, the same instinct behind writing a rollback plan before a risky deploy rather than improvising one during an incident.

  • Circuit Breaker Pattern — both are resilience-adjacent patterns that assume failure/rollback as a normal case rather than an exception, though Memento addresses state recovery specifically

Sources

Hermes Wiki