2026-05-03
Most developers wire user actions directly to the code that performs them — a button click calls deleteRecord(), a menu item calls saveFile(). This works until you need undo/redo, action queuing, macro recording, or audit logging. The Command Pattern solves this by encapsulating a request as an object, letting you parameterize, queue, log, and reverse operations.
A command object has a simple interface: execute() and optionally undo(). Instead of calling business logic directly, you create a command and hand it to an invoker.
Real-world example: a text editor with undo. Without the Command Pattern, your editor has a tangled mess of state-reversal logic scattered everywhere. With it:
execute() inserts it, undo() removes it.execute() removes, undo() re-inserts.execute() applies new format, undo() restores old.The editor maintains a stack of executed commands. Undo pops the top command and calls undo(). Redo pushes it back and calls execute() again. Every new action type you add just needs its own command class — the undo infrastructure never changes.
Where else this shines:
execute(). Failed jobs can be retried or dead-lettered without the queue knowing anything about the job's internals.Rule of thumb: if you have more than 3 distinct actions that need to support any combination of undo, queuing, logging, or deferred execution, introduce the Command Pattern. Below that threshold, the indirection costs more than it saves.
Watch out for common mistakes. The biggest one is commands that capture too much state — your command should store only the delta needed to reverse the action, not a snapshot of the entire system. A command that clones the whole document on every keystroke will destroy your memory budget. The second mistake is letting commands hold references to mutable objects that change between execute() and undo(). Commands should capture values, not references, unless you're certain the referenced object won't mutate.
