The Command Design Pattern — A Complete Visual Guide
What you will walk away with: A deep, intuitive understanding of the Command pattern — not just what it is, but why it exists, when to reach for it, and how the world's best engineering teams use it every day. We will build understanding brick by brick, never skipping a step.
Before We Write a Single Line of Code
Let us have a conversation first.
Have you ever used Ctrl+Z?
Of course you have. You typed something wrong, pressed Ctrl+Z, and it disappeared. Magic. Now think about this — how does the software know what to undo? It does not just delete the last character. It reverses an entire operation. Sometimes a complex one.
That is the Command pattern at work.
Have you ever wondered how a game records and replays your inputs?
You play a match. The game records every button press. Later it replays the exact same match. How? Every input — move left, jump, shoot — was stored as an object. Those objects were replayed in sequence.
That is the Command pattern at work.
Have you ever used a job queue? A print queue, a message queue, a task scheduler?
A task goes in. A worker picks it up later. The task does not execute immediately — it waits. How does the queue store "a task that can be executed later"? As an object with an execute() method.
That is the Command pattern at work.
💡 Key Insight Before We Proceed
The Command pattern is not about commands in the English sense — it is about turning actions into objects. Once an action is an object, you can store it, pass it around, queue it, log it, undo it, replay it.
Chapter 1 — Feel the Problem First
Imagine you are using Microsoft Word.
You type "Hello World". Then you make it bold. Then you make it italic. Then you press Ctrl+Z three times and everything goes back to blank.
Simple. You have done this a thousand times.
Now I want you to think like an engineer. How does Word remember what to undo?
It is not magic. Somewhere in Word's code, there is a list. Every time you do something, an entry goes into that list. When you press Ctrl+Z, Word looks at the last entry in the list and reverses it.
Okay. So far so good. Now the real question — what exactly is inside each entry in that list?
Think about it. Entry #2 is "bold was applied". But how do you store "bold was applied" in a list? A list stores objects. So this entry must be an object! AHA Moment
What does that object need to know?
That object — the thing that knows what was done AND how to reverse it — that is a Command object.
Now let us go one level deeper.
You have a text editor. It has a toolbar button for bold. It has a keyboard shortcut Ctrl+B for bold. It has a right-click menu option for bold.
Three different ways to trigger the exact same thing.
All three lead to the same place. But what is that place?
Without Command pattern, each one directly calls the editor:
Looks fine. But now add undo. Each one needs to also record history:
Every single invoker (button, shortcut, menu) is copy-pasting the same two lines. Now add 10 more operations — underline, strikethrough, font size, color, alignment... Every invoker needs to duplicate logic for all 10.
Now imagine your manager says "we are adding a voice command feature." A fourth invoker. You have to go and add the same duplicated logic there too.
This is the problem. Not a crash. Not a bug. Just a design that causes pain — every new invoker duplicates logic, every new feature touches every invoker. It grows like weeds.
Now watch what happens when we flip the thinking.
Instead of the button calling editor.bold() directly — what if the button just held a small object? And that object knew how to do bold AND how to undo bold. The button just calls go() on that object. It does not care what go() does inside.
Now the button knows nothing about bold. It just knows "call go() on whatever object I am holding."
The button, the shortcut, and the menu all point to the same object. Zero duplication. Add a voice command? Give it the same object. Done.
Add a new operation like underline? Create an UnderlineObject. Give it to whichever invokers need it. The existing invokers do not change at all.
That object — the one that wraps an action, knows how to execute it, and knows how to undo it — is called a Command.
That is the entire pattern.
Everything else — macros, queues, logs, replay — is just a natural consequence of the fact that actions are now objects you can hold, store, pass around, and call later.
Lets look at this again. And lets see what we would have written in a real interview under pressure (if we did not know Command Design Pattern) Here is our editor:
Now we need a toolbar with buttons:
Seems fine. Now add keyboard shortcuts:
Now add a context menu:
Now if the interviewer asks to add undo :)
Stop. Look at what is happening.
This is called tight coupling. Your invokers (button, shortcut, menu) know too much about your receiver (editor). They are entangled.
❓ Question to think about:
What if you could wrap
editor.bold()inside an object — and just hand that object to any invoker? The invoker would not need to know it is callingbold(). It would just callexecute()on whatever object it received.That is exactly what Command pattern does. Ready to see it?
Chapter 2 — The Insight That Changes Everything
Here is the key mental model shift.
Before Command pattern — actions are method calls:
After Command pattern — actions are objects:
The button no longer calls bold() directly. It holds a Command object and calls execute(). It does not care what execute() does internally. It just calls it.
This one shift unlocks everything:
- Any invoker can use any command — just call
execute() - Undo? Add
undo()to the command interface - Queue operations? Store command objects in a queue
- Log operations? Serialize command objects to disk
- Replay? Re-call
execute()on stored command objects - Macro? Group multiple command objects, call
execute()on all of them
Chapter 3 — The Four Players, One at a Time
The Command pattern has four roles. Let us meet them one by one — slowly, with full context.
Player 1 — The Command Interface
This is the contract. Every action in your system will implement this interface.
That is literally it. Two methods. That is the entire interface.
Why undo()? Because the most powerful feature of Command pattern is undoability. Every command knows how to reverse itself.
Why virtual ~Command()? Because we will store commands through base class pointers (Command*), and we need the destructor to be virtual so the correct derived destructor is called.
Player 2 — The Receiver
The receiver is the object that actually does the real work. It has no knowledge of commands whatsoever. It is a pure domain object.
Notice: TextEditor knows nothing about Command, BoldCommand, or any invoker. It is completely isolated. This is good — it can be tested independently.
Player 3 — Concrete Commands
These are the actual command implementations. Each one:
- Holds a reference to the receiver
- Knows exactly what to call on the receiver in
execute() - Knows exactly how to reverse it in
undo()
❓ Quick Check — Why does BoldCommand hold a reference to TextEditor?
Because the command needs to delegate work to the receiver when
execute()is called. Without the reference, it cannot reach the receiver. The reference is injected at construction time — this is dependency injection in its simplest form.
Player 4 — The Invoker
The invoker triggers commands. It knows nothing about the receiver. It only knows the Command interface.
Look at what the invoker does NOT do:
- It does not call
editor.bold() - It does not know what
TextEditoris - It does not know what
BoldCommandis — it only knowsCommand
This is the entire power of the pattern. The invoker is completely generic. You can plug in any command — now or in the future — and the invoker needs zero changes.
Chapter 4 — Putting It All Together, Line by Line
Now let us write the complete working program. We will go line by line.
Output:
❓ Check your understanding:
When we call
invoker.undo()the second time, what chain of calls happens?Answer:
invoker.undo()is called- Invoker looks at top of history stack — finds
BoldCommand- Calls
boldCmd.undo()BoldCommand::undo()callseditor.removeLast(6)[BOLD]is removed from content- Command is popped from stack
Chapter 5 — The Redo Stack
Once you have undo, redo is a natural extension. When you undo a command, instead of discarding it, move it to a redo stack.
💡 Why does a new action clear the redo stack?
Think about it from the user's perspective. You type "Hello", undo it (empty again), then type "World". Can you now redo "Hello"? No — "Hello" is gone. The timeline branched. There is only one timeline — the redo stack represents a future that was abandoned the moment you took a new action.
Chapter 6 — The Macro Command
A Macro Command is a command that contains other commands. When you call execute() on a macro, it calls execute() on all its children. When you call undo(), it reverses them in reverse order.
Usage:
❓ Why must undo be in reverse order?
Imagine you execute: Bold → Italic → Underline
The content becomes:
[BOLD][ITALIC][UNDERLINE]To undo, you must remove
[UNDERLINE]first (the last applied), then[ITALIC], then[BOLD]. If you undo in forward order, you would try to remove[BOLD]from the middle of the string — wrong position, wrong result.Think of it like a stack of plates. You put them on in order A, B, C. You must take them off in order C, B, A.
Chapter 7 — Real World Deep Dive: Adobe Photoshop
Photoshop's History panel is one of the most famous Command pattern implementations in the world. Every operation — brush stroke, crop, filter, color adjustment — is a Command object.
But here is the advanced part — Photoshop filters cannot always be mathematically reversed. You cannot "un-blur" an image by formula. So Photoshop uses the Memento pattern alongside Command — it saves a snapshot of the pixel buffer before the filter is applied, and restores it on undo.
Chapter 8 — Real World Deep Dive: HFT Trading Systems
In High-Frequency Trading, every market action — place order, cancel order, modify order — is encapsulated as a Command. This is not just good design, it is a regulatory and operational requirement.
Why Command pattern in HFT?
The HFT Invoker — A Lock-Free Command Queue:
In HFT, you cannot afford mutex locks in the hot path. Commands are pushed by the strategy thread and consumed by the order execution thread using a lock-free MPSC (Multi-Producer Single-Consumer) ring buffer:
💡 Why pre-allocate commands?
newanddelete(heap allocation) are unpredictable in latency. They can take microseconds — an eternity in HFT where you measure in nanoseconds. By pre-allocating a pool of command objects at startup and reusing them, you eliminate heap allocation from the hot path entirely. This is a fundamental HFT optimization technique.
Chapter 9 — Real World Deep Dive: EDA (Cadence, Synopsys)
In EDA (Electronic Design Automation), simulation runs are extraordinarily expensive — a full chip simulation can take hours or days. Every job (lint check, compilation, synthesis, timing analysis) is managed as a Command.
Chapter 10 — Real World Deep Dive: FAANG (Google Docs)
Google Docs allows multiple users to edit the same document simultaneously. Every keystroke is a Command. When two users edit at the same time, their commands are transformed before being applied so both documents reach the same final state.
This is called Operational Transformation (OT).
Chapter 11 — Command + Thread Pool = Async Task System
The Command pattern is the backbone of every async task system. Worker threads drain a queue of Command objects:
This pattern powers:
- AWS Lambda — each invocation is a command dispatched to a worker
- Chrome's compositor thread — rendering commands dispatched from UI thread
- Kafka consumers — each message triggers a command on a consumer thread
- Database connection pools — each query is a command executed by a pool worker
Chapter 12 — Serializable Commands (Logging and Replay)
Commands that can be serialized to disk enable crash recovery, audit logs, and event replay.
Chapter 13 — Command vs Strategy — The Confusion Cleared
These two patterns are often confused. Let us clear this up once and for all.
See the relationship? A Command can use a Strategy internally. They are not competitors — they are collaborators.
Chapter 14 — When to Use, When to Avoid
Chapter 15 — Modern C++ Shortcut: Lambda as Command
In modern C++ (C++11 and beyond), simple commands can be expressed as lambdas stored in std::function — no class needed:
When to use classes vs lambdas:
| Situation | Use |
|-----------|-----|
| Simple operation, stateless | Lambda |
| Operation needs saved state for undo | Class (can store members) |
| Operation needs serialization | Class (add serialize()) |
| Many similar commands, reused across codebase | Class hierarchy |
| Quick prototype | Lambda |
Chapter 16 — Complete Architecture Diagram
Chapter 17 — Summary: The Five Powers of Command
Chapter 18 — Practice Exercises (Levelled)
Level 1 — Calculator with Undo
Implement a calculator supporting Add, Subtract, Multiply, Divide commands with a full undo/redo stack.
Requirements:
- Each operation is a Command
undo()reverses the operation mathematically (add 5 → undo subtracts 5)- Full undo/redo history
Starter:
Level 2 — Smart Home Controller
Build a smart home system with:
LightOnCommand/LightOffCommandThermostatSetCommandLockDoorCommand/UnlockDoorCommand- A "Goodnight" MacroCommand that: turns off all lights, sets thermostat to 18°C, locks the door
Bonus: A "Morning" MacroCommand that reverses the Goodnight macro.
Level 3 — Simplified Git
Implement a versioned file system:
CommitCommand— saves a snapshot of current filesRevertCommand— restores a previous commitBranchCommand— creates a named branch (fork of history)
Hint: Use the Memento pattern for snapshots.
Level 4 — HFT Order Book (Advanced)
Build a simplified trading command system:
PlaceOrderCommandwith pre-allocated memory pool (no heap in hot path)CancelOrderCommand- A lock-free SPSC ring buffer as the command queue
rdtsc-based latency measurement — measure time from submit to execute- Serialize every command to a log file for audit
Level 5 — Distributed Command Log
Build a command log that:
- Serializes commands to disk on every execute
- On startup, reads the log and replays all commands to restore state
- Supports "time travel" — replay up to a given timestamp
This is how databases implement crash recovery (WAL — Write-Ahead Log).
Final Words
The Command pattern is one of those patterns you will use everywhere once you see it. Every undo stack, every job queue, every audit log, every macro system — it is Command under the hood.
The next time you press Ctrl+Z in any application, you will know exactly what is happening. The next time you submit a job to a queue, you will know it is a Command being stored for later execution. The next time you see a distributed system with event replay, you will recognise the serialized Command log.
That is what understanding patterns gives you — X-ray vision into the systems around you.
What to do next:
- Implement Level 1 without looking at the solution
- Read the source code of an open-source editor (VSCode, Vim) and find their command/action system
- Look at how Kafka consumer offsets work — it is Command + log at scale
- Come back and attempt Level 4 after you have read about lock-free data structures