5

I am planning adding an undo/recover mechanism to an existing application, and I am looking for ideas hwo to present it to the user under pretty ugly restrictions. I've found a few particular hints, this post standing out, but nothing that gives me ideas hwo to approach this.


The application is basically a hierarchy of nodes, each with some insignificant metadata (name, comment, ...) and large data objects that are opaque to the undo/redo mechanism.

Typical operations are

  • create / copy / move / delete nodes
  • edit metadata
  • change / replace node data (some times a few dozen times in a row)

Problems:

  • Existing application with no undo; it does already autosave and thus provides pretty heavy blocking dialogs for destructive actions
  • You can - and are encouraged to - generate tangible amounts of data (a typical spike would be ~1MB/s for 30 seconds)
  • Even though it was never "officially recommended", users are used to concurrent access to the database (no server, via file sharing). Currently, it's "last one wins" and weird errors (and lost data) when someone else deletes the node you are working on.

We are in the process of migrating to a different data store (already decided to be SQLite), I want to get at least "a plan" for undo and understand the necessary changes to data structure.


My thoughts: Due to some nodes being highly volatile, others being "virtually sacred", I don't feel global Ctrl+Z/Ctrl+Y will work well for this. E.g. one small change blocks redoing all your undo's, making an accidental undo possibly fatal.

I am unsure about how to present to user, how to limit the amount of data in a way the user understands, where to store it, etc.

Related ideas are a "trash bin" metaphor for the large data, and/or allowing to mark items read-only, preventing modification.


Ideas? Recommendations? War stories? A link to the canonical "12 different ways how to implement undo, and how to pick yours" that I happen to not find?

3 Answers 3

3

At the risk of rambling - here's a few thoughts.

The first thing to decide is whether you want traditional system wide undo in a historical format or whether you want a more localized per-node undo context. It's the system wide sequential undo/redo that is the root of many problems.

For example, compare editing an image (sequential/historical - last-in-first-out events ) vs Q/As on Stack Exchange Network, with a per-post create, edit, delete, undelete, regardless of what is going on with other nodes (posts, answers)

For sequential undo, where Ctrl-Z, Ctrl-Y is inappropriate an event browser or timeline can be a good presentation. Even for non-sequential undo this can be useful if the information is presented in a clear manner. Imagine a bar chart type presentation where you can mark a whole stack, or n sections of a stack (top-down order) as undone. I would always suggest contextualizing wherever possible and appropriate.

For a localized undo, you have to decide what is the top level node that manages the events and present the interaction at that level - so like a Stack Exchange Answer, you have localized interaction - add/create/delete/undelete and for the sequential editing of data you provide revisions (that's the localized sequential aspect) of that data - not revisions of the system as a whole.

The Stack Exchange Network provide the mechanism you hinted of - that when a post is deleted - is is actually still there for a while in a read only view - pending either an undelete or (I think) a timeout period during which it can be undeleted. So this is essentially a change of state rather than a change in the underlying data itself.

At some point there could be the equivalent of a garbage collection and nodes with a certain status result in an actual change of data. That process can be managed in a safe 'locked down' mode when applicable - depending on system criteria - eg it could be a nightly process - a logout process or a session-end process.

Another common interaction is to simply provide a revert-to-default or undo-changes button, which effectively undoes anything regardless of intermediate revisions. This either requires knowledge of the default state or use of a 'restore point'.

1
  • Thanks for rambling :) At this point, I am looking for random thoughts and strange ideas.
    – peterchen
    Nov 17, 2011 at 12:18
3

Non-global item-level undo may not only be considerably easier to implement, as Roger Attrill suggests in his answer, it may have significant usability advantages. The basic architecture is to link each node or other data object with a record of any change made to it (type of action, timestamp, user, relevant field/attribute if applicable).

With this, one can have Selective Undo, in contrast with the global history-based sequential undo. With Selective Undo, when the user selects an action to undo, only that action is necessarily undone. Subsequent actions are generally unaffected. This works well because most actions are independent –editing the metadata on one node has no impact on editing metadata on another. Chances are even changes to fields within a node have no effect on the other fields in that same node. In cases where there are dependencies, the data may be adjusted to compensate for the undoing of the selected action, which may involve undoing other dependent actions (generally with some kind of user feedback). Exactly what happens and even what “dependent” means is determined by the relations between the actions, and what “undo” means to you philosophically. For details on these issues and how to implement Selective Undo, see:

Berlage T (1994) A Selective Undo Mechanism for Graphical User Interfaces Based On Command Objects. ACM Transactions on Computer-Human Interaction. 1(3) September 269-294

Selective Undo gives the user more control, providing advantages over classical global-historic undo:

  • Users no longer have to discard all their work on Node A just because they realize they made a mistake on Node B before working on A. For that matter, they don’t have to discard changes to Field A just because they need to undo earlier work on Field B.

  • If User 1 deletes Node A after User 2 retrieved Node A for editing, the system can undo the deletion (perhaps with verification or notification to User 2) in order to save User 2’s edits, and not affect anything else that’s going on.

  • Redo is likewise selective, acting only on the selected action. Undo something then editing something else doesn’t preclude redoing what was undone, assuming no dependency.

  • You can include in Undo actions that aren't normally included in Undo but still screw users up, such as changes to preferences and presentation (e.g., filtering, zoom). These are usually left out of Undo to avoid the risk of the user undoing important data changes to fix something else.

As for the UI,

  • You can have an easily discoverable centralized Undo menu that lists all actions the user has done on any node. You will, however, have to communicate to users that only the selected action is undone (except when there are dependencies).

  • You can have a Ctrl-Z accelerator or other input that undoes the latest action based on the timestamp to allow users to step back to a prior state, and likewise Ctrl-Y to redo, so you still support global-history style undo in a familiar manner.

  • You can also have an Undo cascade menu on the context menu for a node or even a field that lists actions to undo/redo for that node/field. This allows the user to zero in quickly on actions for an object that was changed long ago. For undoing node deletions, a recycling bin is certainly an option to support such zeroing in.

  • You can allow the users to open an Undo window or dialog with a table of all actions for undo/redo, which user can search, sort, and multi-select for undo. Users can not only undo a block of sequential actions (like classical undo), they can undo a bunch of actions for a given node or other data object. Users (perhaps administrators) can undo actions of another user (that new guy that really hosed things up). That is especially useful if you have storage space to save undo information between sessions. The window can support previewing the effects of undoing a bunch of actions without committing to it.

1

Continuing the "ramblings".

I once saw the aptly named "Usability supporting architectural patterns" http://www.cs.cmu.edu/~bej/usa/publications/ICSEtutorial04-final.pdf presented at a conference. This stresses from at technical point of view why adding undo/redo at a late stage in a project is a huge task.

1
  • Thanks! In still hope the migration to a new data store is in my favor: I have to transform existing data anyway, and I "can" break backward/forward compatibility for the first time in a decade. Will read...
    – peterchen
    Nov 17, 2011 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.