Command Execution System

Overview

Design a system that registers named commands and runs them on demand. Each command does some work and reports a result; the system keeps a history of what ran, and can undo the most recent commands in reverse order.

The core idea is to treat every action as an object instead of a bare function call — the command pattern. Wrapping an action as an object lets the system store it, log it, and reverse it later. To support undo, each command must know how to apply its effect and how to roll it back, so the system can walk the history backwards and restore earlier state.

Interfaces

A Command contract, plus an executor that runs and tracks them:

class Command:
    def execute(self): ...        # do the work, return a result
    def undo(self): ...           # reverse the effect of execute()
    def name(self): ...           # identifier for history/logging


class CommandExecutor:
    def register(self, name, command): ...   # make a command runnable by name
    def run(self, name, *args): ...          # execute it, record it in history
    def undo(self): ...                       # reverse the most recent command
    def redo(self): ...                       # re-apply the most recently undone
    def history(self): ...                    # ordered list of what has run

run looks up the command, executes it, and pushes it onto an undo stack. undo pops that stack, calls the command's undo, and moves it to a redo stack; redo reverses that. Keeping two stacks gives ordered, reversible history for free.

Operations

  • register(name, command): bind a name to a command so it can be invoked.
  • run(name, ...args): execute the named command, return its result, and append it to history.
  • undo(): reverse the last executed command and return what was undone.
  • redo(): re-execute the last undone command.
  • history(): the commands that have run, in execution order.

Requirements

  • Unknown command names fail clearly rather than silently doing nothing.
  • undo reverses commands in LIFO order — most recent first.
  • Running a new command after an undo clears the redo stack (no branching history).
  • A command with no meaningful inverse may declare itself non-undoable; the system must reject undoing it instead of corrupting state.
  • The design should extend to new command types without changing the executor.

Examples

ex = CommandExecutor()
ex.register("add", AddToBalance(account))

ex.run("add", 100)     # balance 0 -> 100, history: [add]
ex.run("add", 50)      # balance 100 -> 150, history: [add, add]

ex.undo()              # balance 150 -> 100  (reverses the +50)
ex.undo()              # balance 100 -> 0    (reverses the +100)
ex.redo()              # balance 0 -> 100    (re-applies the +100)

Think about it

A command runs "set X to 9", and later you call undo — but the old value is already gone.

What does a command need to capture, and at what moment, to be able to reverse itself?

And is there any command that simply can't be undone?

Follow-ups

  • How would you run commands asynchronously on a worker thread and still let a caller get the result back?

  • How would you bundle several commands into one macro that undoes as a single unit?

  • If you persisted the history to a log, how would you replay it to rebuild state after a restart?

  • And more...

Databricks Crash Course

Fullset Newest Databricks Coding/System Design Questions, Optimal Solutions and Explanations.

Step_1 Matched with a FAANG+ Senior Engineer

Step_2 Fullset Databricks Coding/System Design Questions

Step_3 Solutions + step by step Explanation

Check it out