Only Done When You Can Go Back: Undo, Redo, and Operation History for Vibe Projects
Undo is an operation list, not a time machine. This guide sets intent-based granularity, a seven-field history item, clearing and truncation rules, per-user stacks for collaboration, and a ten-scenario test list. All products and people named are fictional examples.

Fictional examples note: the product "Skylark Docs" and the person "Xiaoyu" below are fictional, used only to illustrate the design process. They do not refer to any real product or user.
Almost everyone who has used an editor has pressed Ctrl+Z, and almost everyone has discovered at some point that what came back was not the step they wanted. Perhaps it went back three steps too many and removed a paragraph just written. Perhaps it could not go back at all, because a save, a refresh, or a colleague's edit in between had cut the history short. Perhaps nothing visibly changed at all — because the operation being undone was never in the undo stack. Undo looks like the most obvious feature in an editor; in practice it is one of the most underestimated pieces of state management. The question is not "can we go back" but "does every step back match the step the user has in mind."
This guide covers exactly that: building trustworthy undo, redo, and operation history for a vibe project. Scope first: this is about in-session operation history — the edits a user makes in one editing session, how they are undone step by step, redone, and presented. It is not soft delete and trash (file-level deletion recovery, covered by a separate guide), not database backup and disaster recovery (system-level recovery, also covered separately), and not version control branching and merging. The battlefield here is the line formed by dozens or hundreds of consecutive operations inside an editor.
Start with the mental model: the undo stack is an operation list, not a time machine
Before building undo, the team must agree on a mental model. In the user's mind, undo is not "return to the whole world as it was five minutes ago" but "take back the one thing I just did." That difference decides the stack's granularity. The right model is an operation list: each complete user intent — typing a sentence, deleting a paragraph, moving a card, changing a field — is one history item; undo removes items from the end one by one, redo restores them one by one. Get granularity wrong and everything else fails: record every keystroke as an item and one undo removes a single character, making undo feel useless; record a whole batch as one item and users who want to undo only part of it have nowhere to go.
The golden rule for granularity is "merge by intent, split at boundaries." Continuous typing merges: when a user types a whole sentence in one go, one undo should remove the whole sentence, not character by character. Merge breaks at pauses and at operation-type switches: the user pauses to think beyond a threshold, or switches from typing to deleting, from editing to moving — a new item begins. Batch operations split by user intent: selecting ten items and changing their category is one intent and one item; but system-generated side effects bundled into a batch must be labeled separately, not silently merged into the user operation — otherwise undo will also revert things the user never touched.
Take the fictional "Skylark Docs": Xiaoyu types three sentences in a row, bolds one of them, and drags a paragraph to another section. Sensible history is four items: typing three sentences (one item), bolding (one item), dragging (one item), plus an earlier deletion (one item). Split the three sentences into dozens of keystrokes and undo collapses; merge bolding and dragging into one item and users who want to undo only the drag collapse too. There is no silver bullet for granularity, but there is a test: have five real users each make ten edits and undo ten times, and record how often they say "yes, that is the step" after each undo. That hit rate is the granularity score.
The data model: one history item must record at least seven things
Undo data models are often written too simply by agents: just "value before" and "value after." That suffices for a single-field form and falls far short in a real editor. A trustworthy history item records at least seven things: operation type (type, delete, move, format, property change); target identity (which object, which field); before and after snapshots (at least of the affected scope, not the whole document); cursor or selection position (where the cursor should land after undo); timestamp; operation source (manual user, automatic agent, collaborating colleague, system side effect); and a mergeable flag (whether this item may merge with the previous one). Miss any one and some scenario will expose it.
The two most commonly omitted are cursor position and operation source. A cursor jumping randomly after undo is the strongest "untrustworthy" signal users feel: the content returned, but the cursor stayed put, and users spend seconds confirming where they are. Source is the lifeline in collaboration: when an agent auto-formats overnight or a colleague edits a field, undo must distinguish "I did this" from "someone else did this" — otherwise one user's undo can revert another's work. History items must carry source, and the interface must surface it somehow, even as a color or a small label.
Snapshot strategy deserves its own decision: full snapshots are simple but expensive; incremental snapshots save space but are complex to implement. The pragmatic middle is "affected-scope snapshots": save only the objects directly changed by the operation and their direct relations, not the whole document. Full snapshots on large documents quickly become a memory and performance problem; but a wrongly written inverse operation for incremental snapshots is more dangerous than a full snapshot. The requirement to give an agent should be: get correctness right with affected-scope snapshots first, then talk about compression and performance. Correctness always outranks performance.
Boundary rules: when to clear, when to truncate, when to refuse
Boundary rules generate the most post-launch complaints and must be fixed at design time. First, a capacity limit: when history items or memory use hit the cap, the oldest items drop — and users must be told, for example with a note in the history panel that "earlier history is beyond the retention range," rather than a silent truncation discovered only when users hit the end of the stack. Set the cap by product: a text editor can allow hundreds of items; an editor handling images or canvases should tighten it, and the cap should be memory-based, not purely item-count-based.
Second, "a new operation clears the redo stack": if a user undoes three steps and then makes a new edit, those three steps can no longer be redone, because the new edit builds on the post-undo state and the old redo path is invalid. This is standard editor behavior, but many AI-generated implementations miss it, producing a hybrid state that is neither old nor new after redo. Write it as a test case: undo, undo, new edit, redo — the redo button must be disabled at that point.
Third, a list of non-undoable operations: publish, send, pay, delete an account, overwrite someone else's submitted content. Once executed, these cannot be reclaimed through the undo stack, and the interface must distinguish them clearly — they should not sit in undo history pretending to be reversible, but require confirmation before execution. Mixing non-undoable operations into undoable history is the most dangerous design: users assume "Ctrl+Z will fix it" and discover it will not. Fourth, session boundaries: after a page refresh, a closed tab, or a device switch, whether undo history survives must be stated plainly to users. The honest approach: in-session history lives only in the current session, and cross-session recovery goes through version history or autosaved snapshots, not by pretending the undo stack survives a refresh.
Collaboration: your undo must never undo someone else's work
In a single-user editor, undo is relatively simple. Under multi-user collaboration, it becomes an order of magnitude harder. The core principle is a single sentence: your undo may only undo your own operations, never anyone else's. An implementation with one global stack — everyone's operations pushed in time order — means user A undoing can revert user B's just-submitted change. In a collaboration product, that is an incident, not a minor flaw.
Two workable approaches exist. The first is per-user stacks: each user's operations form their own stack, and undo only walks back within your own stack; when someone else's operation touched the same object as yours, undo first detects the conflict and refuses with a message like "this content was changed by someone else; please review the latest version." The second is operational transformation: represent operations as transformable instructions and transform them against intervening operations before undoing. That path is more powerful but vastly more complex to implement, and unsuitable for a first version. The recommendation for vibe projects: ship per-user stacks plus conflict detection first, guaranteeing "never undo the wrong thing," before chasing "undo smoothly." Do not build version one into an untunable complex system in pursuit of theoretical perfection.
One more collaboration detail: should agent operations enter the undo stack? The answer is yes, but labeled. If agent auto-formatting, refactoring, or batch edits never enter history, users who notice changed content cannot find who changed it or how to go back, and feel uneasy. If they enter history unlabeled, users undoing will wonder "I never did this step." The right approach: agent operations enter the stack normally, labeled as agent-sourced, styled recognizably in the history panel. Users choose "undo only my operations" or "undo agent operations too." Give users the choice instead of deciding for them.
Interface: history panel, shortcuts, and "what was this step"
Undo's interface needs at least three things: reliable shortcuts, a visible history panel, and readable operation descriptions. Shortcuts follow convention: Ctrl/Cmd+Z to undo, Ctrl/Cmd+Shift+Z or Ctrl+Y to redo (Ctrl+Y is the common Windows redo key; Mac uses Cmd+Shift+Z primarily; docs must show both). Shortcuts themselves are covered by the keyboard-shortcuts guide; the point here is that undo shortcuts must behave identically in every editing context, never silently failing inside some dialog or panel.
The history panel is the trust builder: it turns an invisible stack into a visible list. Each item needs one human-readable line — "Typed: project goals paragraph," "Deleted: section 3.2," "Moved: card from To Do to In Progress" — not an internal identifier like "update node #4821" that only developers can read. Description rules must be set: operation type plus object title plus key information, with long titles truncated but still recognizable. The panel should also support "jump to a step": clicking any history item returns directly to that state, rather than forcing step-by-step undo. Implementation-wise that is a combination of several undos or redos, but for users it is the essential difference between a "history" and a "stack": a stack can only be popped from the top; a history can be pointed at anywhere.
Another interface detail: undo and redo button states must reflect stack state in real time. Empty undo stack, greyed-out undo button; empty redo stack, greyed-out redo button. A button that lies — bright while nothing can be undone — is where trust starts to break. After an undo executes, the interface should give light feedback: cursor returns to the pre-undo position, restored content briefly highlights, the current pointer in the history panel moves one step. Each feedback is light, but together they tell users: "that step just happened, and it happened where you meant it to."
Test list: ten scenarios to run before shipping
Before shipping undo, run at least ten scenarios, each with an automated or recorded manual test. One: after continuous typing, one undo removes the whole sentence. Two: type, bold, drag once each, then three undos return exactly to the start. Three: after two undos and a new edit, the redo button is disabled. Four: after deleting an object, undo restores it with its original properties intact, not as an empty shell. Five: after undo, the cursor lands somewhere sensible, not in a screen corner. Six: after a page refresh, history follows the stated rule (kept or cleared) and the interface tells users honestly.
Seven: under collaboration, user A's undo never reverts user B's operation, and conflicts produce a clear message. Eight: after agent operations enter the stack, users undoing can tell sources apart. Nine: when the capacity cap is reached, the oldest history drops by rule, and the panel notes that earlier history is out of range. Ten: non-undoable operations (publish, send) never enter the undo stack and require confirmation before execution. Only when all ten pass can undo be called "usable." Any scenario shipped fuzzy becomes, in real users' hands, one more "I will never dare press Ctrl+Z again" lesson.
Version one: build only these five things
When prioritizing for a vibe project, version one builds only five things. One: single-user in-session undo and redo, merged by intent, covering type, delete, move, and format. Two: cursor restoration after undo and the redo-stack-clearing rule — the correctness floor; without them the stack is a half-product. Three: a history panel with human-readable descriptions, so users can see the road they walked. Four: a non-undoable operations list with confirmation, stating up front what cannot be taken back. Five: a capacity cap with a truncation notice, so history cannot grow unbounded and drag down performance. With those five done, undo can ship and face real inspection; per-user collaboration stacks, jump-to-step, and cross-session recovery can all wait for version two. Make the single-user case rock solid first, then attack collaboration complexity.
Two more implementation traps deserve a place in the task description. The first is undo versus autosave interference: autosave writes intermediate states to the backend while the undo stack still lives in frontend memory, and once their views of "current state" diverge, you get the ghost phenomenon of "the interface shows undone, but refresh brings it back." The fix: autosave must follow the undo stack's current state, not the last edit; on save failure, the undo stack must not roll itself back silently but must tell the user plainly. The second is undo versus form validation interference: when undo returns to a state that was once valid but violates a rule that changed since, what should validation do? The answer: the undo itself must not be blocked by the new rule — users have the right to return to a past state; but once they edit or submit again from that state, the new rule applies normally. Conflating "return to the past" with "submit new content" is the root of many form-undo bugs.

Back to Xiaoyu's "Skylark Docs": she edited all morning, pressed undo a dozen times, and every single one landed exactly on the step she had in mind. Every history-panel line read in words she understood, and the agent's changes were clearly marked. She will not praise your undo design for that — she will simply feel "this editor is steady." And "steady" is undo's only, and highest, standard. Undo is not a place to show off; it is the precondition for users daring to change things: only when users are sure a mistake can be taken back will they dare to move forward. Make undo steady, and you hand users back their courage.
Comments (0)
Related articles

Shortcuts fail when they are listed before they are layered. This guide layers operations by frequency and risk, borrows standard keys, avoids browser, IME, and screen-reader collisions, and ships a prompt system plus a launch checklist. All products and people named are fictional examples.

Push, email, and an in-app notification center are three different things — merging them just gets everything muted. This guide gives a three-tier triage, four logical tables, inbox interactions designed around 'cleared,' and three metrics. All products and people named are fictional examples.

Deletion is a state machine, not a button: request, disclosure, grace period, erasure, confirmation, review. This guide gives a three-way scope table, a six-state flow, and a ten-item engineering checklist — plus the backups, indexes, third parties, and billing most teams miss. All products and people named are fictional examples.