When You Want to Serialize User Actions - Unexpected Benefits of Command Pattern

While implementing "save immediately to server upon field edit" in a task management system, I hit a wall - "how to handle edits during offline". The Command pattern not only solved this but brought unexpected benefits.

Toshiki Matsukuma | 2026-02-27 | 5 min

While implementing “save immediately to server upon field edit” in a task management system, I hit a wall. The wall was: how to handle the situation when “user edits a field during offline”.

Simply waiting for network recovery would confuse users about what was saved and what wasn’t.

Why Command Pattern?

The requirement was: during offline, store user operations and execute them in order once network recovers.

// User operation: Change task title
const command = {
type: 'UPDATE_TASK_TITLE',
taskId: '123',
newTitle: 'Updated title',
timestamp: Date.now()
};
// Store in queue during offline
offlineQueue.push(command);
// Execute in order when network recovers
await executeCommands(offlineQueue);

Structuring operations this way made it possible to store and replay them. This is exactly the Command pattern (encapsulating operations as objects).

Unexpected Benefits

Initially I only wanted offline support, but I gained additional benefits:

1. Undo/Redo Became Trivial

// Undo history
const history = [cmd1, cmd2, cmd3];
// Undo
const lastCommand = history.pop();
await lastCommand.undo();
// Redo
history.push(lastCommand);
await lastCommand.execute();

Since operations were objects, implementing undo/redo was just a matter of managing a stack.

2. Testing Became Easier

// Test: Does title update work correctly?
const command = new UpdateTaskTitleCommand('123', 'New title');
await command.execute();
expect(getTask('123').title).toBe('New title');

Since each operation was an independent object, I could test them in isolation.

3. Could Implement Optimistic UI

// Immediately reflect in UI (optimistic update)
updateUIOptimistically(command);
// Execute in background
command.execute()
.catch(() => {
// Rollback if failed
revertUI(command);
});

Separating “execute” from “display” made implementing optimistic UI natural.

Summary

While implementing “save immediately upon field edit” in a task management system, I hit a wall with “how to handle edits during offline”. Using Command pattern not only solved this but brought unexpected benefits like easy undo/redo implementation and simplified testing.

Particularly, structuring operations as objects made many frontend patterns (offline queue, optimistic UI, undo/redo) work naturally together.

関連記事

tosh.sh

tosh.sh — Notes on design and implementation

Tech Stack

  • Astro
  • React
  • Framer Motion
  • Three.js
  • TailwindCSS
  • shadcn/ui

© 2026 tosh.sh All rights reserved.