AcademyDesign Patterns
Command pattern
Turning "do this" into an object that can be queued, logged or undone, the class every action now needs, and a command that reaches into global state instead of its own.
6 min readUpdated 3 exercises
Command is the pattern for treating a request as data instead of an immediate function call, which is what makes undo stacks and task queues possible in the first place.
A Command wraps "do this specific thing" as an object with an execute() method, instead of a direct function call. That turns a request into a value, one that can be handed to something else, queued, logged, or run later, without the thing invoking it needing to know what it does.
The problem it solves
A remote control has buttons. Each button should turn on a light, open a garage door, or change a channel. If the button code calls those devices directly, the button class knows about every device:
press() {
if (this.mode === 'light') light.turnOn();
else if (this.mode === 'garage') garage.open();
}- The invoker is coupled to what it triggers. Adding a device means editing the button.
- Nothing can be undone. A direct call leaves no record of what happened.
- Nothing can be delayed. The call happens now, or not at all. You cannot queue it, retry it or run it on another machine.
The fix is to put each action in its own object with an execute() method. The button holds a command and calls execute(), whatever it does.
Structure
Four parts:
- The command (
LightOnCommand): an object representing one action, withexecute(). - The receiver (
Light): the object that does the real work. - The invoker (
RemoteButton): triggers the command without knowing what it does. - The client, which creates commands and connects them to invokers.
class Light {
turnOn() { return 'light-on'; }
}
class LightOnCommand {
constructor(light) { this.light = light; }
execute() { return this.light.turnOn(); }
}
class RemoteButton {
constructor(command) { this.command = command; }
press() { return this.command.execute(); }
}A button can be given a different command at any time, and the same command can be triggered by a button, a keyboard shortcut or a voice assistant. The invoker and the receiver never meet.
Where it is used in real systems
Once a request is an object, three things become easy:
- Undo and redo. Add an
undo()method to each command, and keep executed commands on a stack. - Queues and scheduling. Store commands and run them later, in order, on a worker.
- Logging and replay. Serialize commands, and you have an audit trail you can replay to rebuild state.
| Use case | The command |
|---|---|
| Text editors | Bold, paste, delete, each with an undo(). |
| Background jobs | A job payload such as "send this email", processed by a worker (BullMQ, SQS). |
| Redux and event sourcing | An action object describing what happened, replayed to rebuild state. |
| Database migrations | Each migration has an up and a down. |
| Macros and shortcuts | A list of commands run in sequence. |
| CLI tools | Each subcommand (git commit, npm install) is a command object. |
| Transactions | Operations collected, then applied together or rolled back. |
Commands you already use
- Redux actions.
{ type: 'cart/add', payload }is a command as data, dispatched to a reducer. - Job queues. A message in BullMQ or SQS is a serialized command that a worker executes later.
- `Function.prototype.bind` and closures.
() => light.turnOn()is a command in its lightest form. - VS Code and editor command palettes. Every entry is a named command that can be bound to a key.
- Database migrations. Each one is a reversible command applied in order.
Command or just a function
In JavaScript a function is often enough. A command object earns its place when you need more than "call it now":
| You need | A plain function | A command object |
|---|---|---|
| Run it now | Fine | Overkill |
| Undo it | Awkward | undo() on the same object |
| Store or send it | Closures cannot be serialized | Plain data, can be stored or sent |
| Log or inspect it | Opaque | Has a name and parameters |
| Queue and retry | Possible | Natural |
If none of these apply, use a function.
When not to use it
- A simple callback is enough. One-shot click handlers do not need command classes.
- Too many tiny classes. One class per action can bloat a codebase. Group them, or use data plus a handler map.
- Undo is hard to implement. Some actions, such as sending an email or charging a card, cannot truly be undone. A command's
undo()for them would lie. Use a compensating action (a refund) and be explicit. - Commands hold live references. A serialized command cannot carry an open connection or a function. Store identifiers and rebuild.
What interviewers ask
- "What is the Command pattern?" Encapsulating a request as an object, so it can be passed around, stored, queued and undone.
- "How do you implement undo?" Each command has an
undo(), and executed commands are kept on a stack. - "Command versus Strategy?" Strategy chooses how something is done. Command represents a specific request to do something, possibly later.
- "Where do you see it in a web app?" Redux actions, job queues, editor commands.
- "How does it help decoupling?" The invoker knows only
execute(), never the receiver.
The mistake to avoid
A Command that does its own work instead of delegating to the receiver. If LightOnCommand contains the logic for turning a light on rather than calling light.turnOn(), the command is no longer a thin wrapper around a request, and the same behavior cannot be reused from anywhere else. The command should carry the request, the receiver should do the work.
The mechanism above, a request represented as an object with one method, is the same in JavaScript, Python or Java. Only the syntax changes.
Now practice it
1 challenge · 2 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- CommandProWrap a request as an object with its own execute() and prove the caller never needs to know what it does — the decoupling this page's diagram shows.ChallengeSolve · MediumChallengeSolve · Medium
- Design Patterns · Behavioral PatternsTen quick questions across Strategy, State, Observer, Command and the rest of the behavioral family, to check the mental model before this one.QuizTake · HardQuizTake · Hard
- Design Patterns · Behavioral Patterns — Series 3ProA second, tougher pass on the same behavioral family, for once the first set feels easy.QuizTake · HardQuizTake · Hard
Read next
Memento pattern
Turning "go back to this" into an object kept separate from undo logic, the snapshot cost that grows with saved state, and a setter turning a saved state into a mutable copy.
6 min
Chain of Responsibility pattern
Trying handlers one at a time until one accepts the request, the dead end when a request reaches the last link unhandled, and why every chain needs a terminal case.
7 min