Symptom

Operations on an object are method calls — they exist only as transient verbs that happen and disappear. Adding undo, macro replay, remote dispatch, queuing, or audit logging requires retrofitting an operation-as-object surface that the existing method-call shape does not provide.

Goal

Each operation is a first-class object with execute() and (where reversible) undo(). Operations are storable, queueable, serializable, replayable, undoable; the receiver (editor, account, queue) executes commands as a uniform interface.

Before the pattern

class TextEditor {
constructor() {
this.text = '';
}
type(s) {
this.text += s;
}
delete(n) {
this.text = this.text.slice(0, -n);
}
}
// Adding undo: we have no record of what was done, so undo is impossible.
// Adding macros (replay 5 operations): we have no first-class notion of
// an operation. Adding remote dispatch (send a command to another editor):
// we'd have to invent a serializable representation by hand.

After the pattern

class TypeCommand {
constructor(editor, text) {
this.editor = editor;
this.text = text;
}
execute() {
this.editor.text += this.text;
}
undo() {
this.editor.text = this.editor.text.slice(0, -this.text.length);
}
}
class DeleteCommand {
constructor(editor, n) {
this.editor = editor;
this.n = n;
this.removed = '';
}
execute() {
this.removed = this.editor.text.slice(-this.n);
this.editor.text = this.editor.text.slice(0, -this.n);
}
undo() {
this.editor.text += this.removed;
}
}
class TextEditor {
constructor() {
this.text = '';
this.history = [];
}
execute(command) {
command.execute();
this.history.push(command);
}
undo() {
const cmd = this.history.pop();
if (cmd) cmd.undo();
}
}
Example source: Illustrative example written for this site in the spirit of Design Patterns (Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1994), chapter 5. The book's running example is a menu-item framework with PasteCommand etc.; this JavaScript adaptation uses a text editor with TypeCommand and DeleteCommand because the undo motivation is concrete and the redo/macro/replay generalizations follow directly.
Pressure

Method-call operations are not data. Adding any cross-cutting concern (undo, audit log, batch dispatch, network replay) requires either invasive per-method instrumentation or replacing the call with a Command object after the fact — the second migration is the more disruptive.

Tradeoff

Each operation becomes a class with execute + (optional) undo; what was a one-line method call is now a class definition + a usage site. The class count grows with operation count; tests for the receiver still exist, plus tests for each Command's execute/undo symmetry.

Relief

Undo, macro, queue, replay, and audit are all generic over Command. Adding any of those features happens once, at the receiver, not once per operation. New operations land as new Command classes without editing the receiver or any cross-cutting feature.

Trap

Commands that hold references to mutable receiver state and rely on receiver state not changing between execute() and undo() produce undo bugs no test catches. A DeleteCommand whose undo restores 'whatever was deleted earlier' must capture the deleted text at execute() time; getting this wrong on a single Command produces silent corruption that survives reviews and ships.