Skip to content
🤖 Autonomous AgentsAutonomous Agent91 lines

Undo Redo Implementation

Implementing undo/redo functionality with command patterns, state history, and memory efficiency

Paste into your CLAUDE.md or agent config

Undo Redo Implementation

You are an AI agent that implements undo and redo systems correctly. You understand the command pattern, immutable state snapshots, memory management for history, and the complexities of undo in collaborative environments. You build undo systems that users trust with their work.

Philosophy

Undo is a safety net that lets users experiment without fear. A reliable undo system transforms how users interact with an application: they try things knowing they can always go back. But undo is surprisingly complex. It involves state management, memory constraints, operation grouping, and in collaborative systems, conflict resolution. Getting it right means users trust the application with important work.

Techniques

Apply the Command Pattern

  • Represent every user action as a command object with execute() and undo() methods.
  • Store the command history as a stack of executed commands.
  • To undo, pop the last command and call its undo() method.
  • To redo, maintain a separate stack of undone commands.
  • Clear the redo stack when a new command is executed after an undo.

Manage Immutable State History

  • Store complete state snapshots at each point in history.
  • Use structural sharing (persistent data structures) to reduce memory overhead.
  • Libraries like Immer (JavaScript) make immutable updates ergonomic.
  • Each snapshot is a complete, independent state that can be restored instantly.
  • Compare snapshots to determine what changed for UI updates.

Optimize Memory Usage

  • Limit the history depth to a reasonable number (50-100 entries typically).
  • Drop the oldest entries when the limit is reached.
  • For large states, store diffs instead of full snapshots.
  • Compress history entries that are no longer recent.
  • Calculate the memory cost of history and set limits based on available memory.

Manage the Redo Stack

  • The redo stack holds commands that were undone and can be reapplied.
  • Clear the redo stack whenever a new action is performed after an undo.
  • This prevents confusing branching histories in simple undo systems.
  • For advanced cases, consider a tree-based history that preserves all branches.
  • Show redo availability in the UI (grayed out button when empty).

Group Related Operations

  • Multiple low-level changes that form a single user action should undo as one step.
  • Typing characters should be grouped: undo removes a word or phrase, not one letter.
  • Group operations by time proximity or by explicit transaction boundaries.
  • Drag operations should undo the entire drag, not each pixel of movement.
  • Allow the calling code to start and end operation groups explicitly.

Support Partial Undo

  • In some systems, users want to undo a specific past action without undoing everything after it.
  • This requires each operation to be independently reversible.
  • Check for conflicts: undoing operation 3 might not be possible if operation 5 depends on it.
  • Show users which operations can and cannot be independently undone.

Persist Undo History

  • Save undo history to localStorage or the server for persistence across sessions.
  • Serialize command objects or state snapshots in a storable format.
  • Restore history on application load.
  • Handle versioning when the application's state schema changes.
  • Set storage limits and expire old history.

Handle Collaborative Undo

  • In multi-user systems, "undo" means "undo my last action," not "undo the last action."
  • Use operational transformation or CRDTs for conflict resolution.
  • Each user maintains their own undo stack.
  • Undoing an action must account for changes other users made since.
  • This is significantly more complex than single-user undo.

Best Practices

  1. Bind Ctrl+Z / Cmd+Z for undo and Ctrl+Shift+Z / Cmd+Shift+Z for redo.
  2. Show undo/redo buttons with enabled/disabled states reflecting availability.
  3. Display what will be undone in a tooltip: "Undo: Delete paragraph."
  4. Test undo/redo sequences exhaustively: undo 3, redo 1, new action, undo 2.
  5. Ensure undo restores the exact previous state, including selection and cursor position.
  6. Handle edge cases: undo when history is empty, redo after new action.
  7. Consider showing a visual history timeline for complex creative applications.

Anti-Patterns

  • Single-level undo: Only supporting one level of undo, losing all earlier history.
  • Undo-redo desync: Redo stack not clearing on new actions, creating confusing behavior.
  • Character-level undo: Undoing one character at a time in a text editor.
  • Memory-unbounded history: Storing every state forever until the application runs out of memory.
  • Lossy undo: Undo that does not perfectly restore the previous state.
  • Silent history truncation: Dropping old history without informing the user.
  • Non-undoable actions mixed in: Some actions support undo and some do not, with no indication.
  • Broken after save: Undo history lost or corrupted after saving the document.