Deep Dive
low level designdesign patternsoop

Command Pattern: Make a Request Something You Can Hold in Your Hand

A method call happens once and is gone. Turn that request into an object with execute() and undo(), and suddenly you can queue it, log it, retry it, and reverse it — the backbone of undo/redo, job queues, and transactional replay.

·15 min read
Medium

Someone on the payments team writes mailer.send(email, receipt) inside the order handler. It works in the demo. Then production happens: the SMTP box hiccups for ninety seconds at 3 a.m., forty receipts throw mid-send, and the on-call engineer is asked a reasonable question — “can you just resend the ones that failed?” — to which the honest answer is no.

Not because sending is hard. Because the request never existed as a thing. It was a stack frame: assembled from local variables, executed once, and unwound the instant send returned or threw. There is nothing on disk, nothing in a queue, nothing to point at and say “retry that one.” The same wall shows up everywhere a request is just a method call: an editor that can’t undo, a job runner that can’t survive a restart, an audit log that has nothing to log.

The fix is almost too simple to sound like a pattern. Stop calling the method. Package the call — the receiver, the arguments, the “what to do” — into an object, and hand that around instead.

The intuition: the order ticket, not the shout

Picture a busy kitchen. A waiter doesn’t lean through the pass and shout “make a medium-rare ribeye” — the cook would be mid-sauce and it’d be gone. The waiter writes an order ticket and spikes it on the rail.

That ticket changes everything, and it’s worth being precise about why. The request is now a physical object: it can wait in a queue on the rail, be worked in order, be re-fired if a plate comes back wrong, be voided by the manager, and it survives as the record of what was asked. The cook — the one who actually knows how to sear a steak — is the receiver. The rail is the invoker: it holds tickets and fires them, without knowing how to cook anything. Making the request concrete is the entire trick.

From here the members-only walkthrough makes it concrete: the four roles diagrammed, the full receiver/command/invoker code with the one snapshot detail that trips everyone up, a run-undo-redo trace, the Command-vs-Strategy trap, the real-world sightings (executors, job queues, the CQRS command bus), the tradeoffs, and an interview corner with a shape-editor undo/redo challenge and a quiz.

Members only

Keep reading with Premium

You've reached the members-only part of this deep-dive — the full implementation, the interactive ring simulator, and the step-by-step walkthrough. Unlock it with a membership.

Related Articles