Design layer document with undo, redo and batches

0
Sign in

You are building the core of a design tool. A document holds layers. Each layer has a unique integer id and a set of string properties, such as color, shape or size. A layer does not need to be created first. It exists from the first time a property is set on its id.

Implement a Document class. The interview usually adds parts one at a time.

Part 1: edit, read, undo

  • Document() creates an empty document.
  • apply(layer_id: int, key: str, value: str) -> None sets key to value on that layer. It creates the layer if the layer does not exist.
  • get_layer(layer_id: int) -> dict[str, str] | None returns the layer's properties, or None if the layer does not exist. Changing the returned dict must not change the document.
  • undo() -> bool reverts the most recent change that has not been undone yet, and returns True. If there is nothing to undo, it returns False and does nothing. If undo reverts the change that created a layer, the layer no longer exists.

Part 2: redo

  • redo() -> bool applies the most recently undone change again. It returns False if there is nothing to redo.
  • Any new apply after an undo throws away everything that could have been redone.

Part 3: batches

  • begin_batch() starts a group. commit_batch() ends it.
  • Edits inside a batch take effect immediately. After the commit, the whole batch is a single step for undo and redo.
  • Committing an empty batch creates no step. Calling undo or redo while a batch is open is an error.

Constraints

  • Up to 10^5 operations in total, and documents with 10^4 or more layers.
  • apply, undo and redo should not take time proportional to the size of the whole document.

Example 1

d = Document()
d.apply(7, "fill", "green")
d.apply(7, "fill", "black")
d.apply(7, "radius", "4")
d.get_layer(7)   -> {"fill": "black", "radius": "4"}
d.undo()         -> True    # {"fill": "black"}
d.undo()         -> True    # {"fill": "green"}
d.undo()         -> True    # layer 7 no longer exists
d.get_layer(7)   -> None
d.redo()         -> True    # {"fill": "green"}
d.apply(3, "opacity", "50")
d.redo()         -> False   # the new edit cleared redo history

Example 2

d = Document()
d.apply(1, "x", "0")
d.begin_batch()
d.apply(1, "x", "10")
d.apply(2, "y", "20")
d.commit_batch()
d.undo()         -> True    # layer 1 is {"x": "0"}, layer 2 is gone
d.redo()         -> True    # layer 1 is {"x": "10"}, layer 2 is {"y": "20"}
FigmaCodingMediumLast asked Jul 2026

Sign in to see the worked answer

The full answer, chapter by chapter, with what interviewers expect at each level. Free with an account: one full answer per company.