Undo
sachagreif.com
sachagreif.com
No, it's not as clean or as good of an experience for your users, but it works great for applications such as email that perform fundamentally irreversible actions.
Since send is making the change in an external system it has to be simulated, but that doesn't change the point of the post.
Gmail can't "undo" once that job has been processed - in other words, when something has been actually done.
What Google is doing is (at least for deletes on mails) is equivalent to a database transaction rollback. So they have a tiny (likely 1-entry) transaction log that they keep per user, and if the user desires, they can use it to fully reverse any operation that is undoable aka reversible.
I suppose it works different for email sends you can undo - in that case it is job cancellation.
Using the optimistic locking pattern, this would mean that Alice could undo, and if Bob then makes changes referring to Alice's entry, it'd be pointing to a previous versioned entry (Alice's undo would simply copy version n-2 and make it version n while Bob would be pointing or editing version n-1)
Rails has a great gem that adds this feature with a minimum of fuss (https://github.com/airblade/paper_trail). There's even a Railscast that goes over how to use it to implement "undo" (http://railscasts.com/episodes/255-undo-with-paper-trail).
Executing a DELETE statement and deleting a bunch of data is simple. Implementing "undo" is more difficult, especially for browser based software. How long is the undo valid for? Can you undo every action? How many levels of undo do you support? Do you allow an undo for UPDATE's as well (updating a blog post to set it's content is essentially deleting it, and would probably be something you want undone, for example).
On top of just building the functionality in to your app, communicating it to your users is also difficult since the web app medium is so different than the desktop one.
Where on Earth do you think labeling a button "Delete" is a legally binding specification with an incredibly specific required implementation?!
It may not be "legally binding", but it certainly is a big risk: look at all the hoo-ha when (e.g.) Facebook/Adobe/etc. don't really delete your profile.
Note that I'm not saying they should/shouldn't: I'm arguing that there's many cases where indicating something is deleted (and not deleting it) can cause user outrage.
Oh, definitely; that's a great way to piss people off. But jd007 specifically mentioned that it might be "downright illegal", and that's what I was criticizing.
In the UK, for example, this is implemented by the Data Protection Act 1998: http://en.wikipedia.org/wiki/Data_Protection_Act_1998
The guidelines for complying with the DPA talk a bit about deletion here: http://www.ico.org.uk/for_organisations/data_protection/the_...
"If you offer users the option to delete personally identifiable information uploaded by them, the deletion must be real i.e. the content should not be recoverable in any way, for example, by accessing a URL from that site."
In fact the DPA takes it even further by requiring that personal data "shall not be kept for longer than is necessary" ie. you are supposed to delete any personally identifying data once you're done using it for it's original purpose (or another legal use). Granted, I imagine it would be pretty hard to bring a case against a company for either of these things, and I can't tell if the former is legally binding or just a "guideline," but it's not outside the realm of possibility...
I think you could easily argue that "needing it for an undo feature" is still a legal use for that data. But, thanks for pointing out that "delete" is a lot more legally loaded a term than I had assumed. It makes a pretty convincing case to call things "remove" instead of "delete".
What if the record you deleted was subject to some kind of uniqueness constraint. If another user has added a record that has the same data, then flipping the delete flag off again would not violate that constraint.
Sure, it could be handled by checking if the constraint is violated. but you rapidly run into a lot of code to implement that one feature and dealing with thinking of and testing all the edge conditions makes it much easier (& cheaper) to focus on other features.
Undo and redo aren't important features for every web app - in some they would't make sense at all - but for applications that facilitate any kind of creative process, such as photo editing, 3d modeling, or drawing, they are very important features to include. Users often want to try out an idea, then go back to an earlier version if the idea doesn't work out. In most cases for apps like these implementing undo/redo gives you a ton of usability bang for your proverbial buck.
Hilarity ensues when someone tries to pretend that a message never existed by cancelling an e-mail sent outside Outlook's garden. (The non-Outlook user will get both the original and the "previous message doesn't exist, okay? Wink wink, nudge nudge" message.)
Okay, so I have a question. Isn't this just a property of the internet as a system?
If we remember the history of human-computer interaction on 2D displays, we first really remember what led to the desktop GUI (Xerox Alto, all that stuff). This domain had tons of research poured into it (of which Raskin is a member) - including from the military, for usability in extreme situations - and by now it's reached a pretty mature stage, and we have developed a nice design language to describe it and advanced technological tools to implement that language. On my Finder, or in Pages, I can reliably undo things nicely. Just today, I deleted a file by mistake- command + z, boom, undone.
(by the way, this is exactly what Apple and mobile devs talks about when they say that well done native apps can never be beat by web apps- those decades of best practice experimental research)
Web apps, on the other hand, run on the internet, which adds some constraints structure wise. All of a sudden, we have at least 2 computers talking to one another, and in most cases many, many more than that. So we have to come up with design principles to make all of this somewhat work together, and we come up with things like RPC and REST. Those things are how our servers talk now, and they influence every technology that we build. They come with some constraints however- notably the ideal of statelessness, in which implementing "undo" would be really hard. Relational databases don't have the semantics to express "undo the last statement that was a user action done with id N", and good luck implementing things like undo in a system where you have caches you can't invalidate, content determined by one way hash functions, etc. If you have a binary blob of 5 lines, that user 1's action modify the first 3 lines, that user's 2 action modifies the last 3, and that user 1 then decides to undo his command?
You'd basically have to version everything, and have super amazing algorithms doing bisects and diffs on your content.
The http internet is really just a way to exchange documents. We've hacked it, using fancy stuff like AJAX, to make it behave more like the traditional GUI that we know and love, but at the core it's still a way to transfer documents. And naturally, HTML, the description language we use for web content, has semantics for describing static documents. Heck we've had to design a whole new language for design (CSS), because it's really just a way to describe documents! So ultimately it lacks the way to implement concepts like undo-ing as first class features.
Let it be noticed that mobile apps do undo-ing very poorly, as well.
I suppose you can also do something state-based like /image/state4 going back to /image/state3. This would push the storage of the edit stack to the server.
Is this what you mean?
But the usability probably wouldn't be fantastic.
In your example, if user 1 doesn't see the edits of user 2 there's a bigger problem. Undo would be liniar.
The product is hardcore JS single page application backed by document oriented database, JSON, REST, buzz, buzz, buzz, etc.
For example, your app inserts a new doc, some other process sees the new doc and updates a table or inserted into a message queue, it gets picked up by Foo and..
and... what? you mean what would happen if I undo the creation of the doc? Like I was deleting the doc - the system either would not allow me because of the linked docs/events/objects or would do a cascade delete, if it can.
Seems like delaying the action will almost always be possible. Guess i could give the users the misconception that they can undo whenever they want, which is just..
It doesn't change the fact that Undo is an amazingly useful feature but its not always as simple as 'delay the action'.
For many cases it would be better if the underlying data storage mechanism would support versioning. Image a CMS that's running on top of git. You'd have diffs, history, branching, merging, clones, tags and other very important features out of the box. Doing those features on top of a relational database is a lot of work, but with git you get them free.
Then you have the persistent data structures from clojure. Now I might be crazy, but it seems to me that those data structures are probably the most valuable contributions that language has made to our craft, and make the sort of things you're talking about-- not trivial, but a lot easier.
The whole trick is you have to start thinking 4-dimensionally- Which is difficult because our programming languages tend to have modify-in-place mutable state, when what we should have is variables that remember what they were at specific times, and algorithms that are able to merge conflicts seamlessly- not by locking but by recreating what would have happened if the two events had happened synchronously on the same system.
But then, you only really have to do that if what you're talking about is a multiuser system. You can do all this much easier if what you're willing to build is a simpler system. One that works like a one page JS app- JUST LIKE A DESKTOP APP, and only talks to the server at the point of "saving" or "synchronising" or whatever you want to call it. That's easy. You just program undo just like you do in a desktop app, and what you save to the server is a serialised history.
On the server, you have your git-like versioned databases for maximum undo. We know this can work because dropbox does it. If I delete a file on my machine... like REALLY delete it, dropbox still has it for 30 days.
Wikipedia articles have "infinite" undo.
And that's all web. that's all internats.
Fundamentally, once you have javascript, the html5 appcache, localStorage, what is the difference between the "web app" and the "native app" other than performance and some other native features irrelevant to undo?
the mobile apps still have to phone home to a server too now. Half the apps on the app store don't work without a net connection. What is the actual difference?
In fact, you could write a whole app that just downloads to the phone, runs entirely locally, doesn't talk to the server after the initial download, and runs in a browser. Why couldn't you have undo in that?
Well, why not. so I added it to my app pix.pe[2] when you press the back button. not exactly the most impressive example since you don't have much state to store... but what's the difference between doing it there, and doing the same thing in a native app? it's all just turing complete language code, with a storage mechanism and a way to draw on screen. Once you have those things there's nothing to stop you. Other than undo itself being kind of difficult already, regardless of what platform you're writing into.
[1]: http://sharejs.org/ [2]: http://pix.pe/
The trick is to put everything in commands which keep track of the old ad the new state (or at least the parts you need), so that you can have a command history stack, and undo merely means "run the unexecute method of the command at the current position, and move the pointer to the previous command". If unexecute fails, you throw away all older commands (undo history). If execute fails during redo, you throw away all newer commands (redo history). Multi-level undo does need to be designed in from the start, but once you learn how it works it does not add that much complexity.
The reason that you don't see many web applications that implement this is because web development did not branch off from desktop development, but instead branched off from unix systems administration (by creating glorified shell scripting languages to output html pages, and gradually improving on that). Web developers never learned from their senior desktop developers how to implement a multi-level undo/redo using a command pattern, because the vast majority of web developers never had a senior desktop developer to learn from. I only learned it myself because I did have such a desktop development mentor. Mobile developers are typically repurposed web developers, so no surprise that it suffers from the same affliction.
A lot of our modern infrastructure comes from the almighty UNIX era- before GUIs had really become as mainstream as there are todays. In that era, things like "undo" were close to non-existent (there is no undo for `rm`- the GUI's "hack" to solve that is the "Recycle Bin" (which is also nonexistent on mobile, interestingly enough)).
A lot of our modern web tools are mere interfaces to those various Unix utilities (phpmyadmin, as mentioned by Sacha, is just a neat little HTML interface to mysqladmin), and thus can't do much to provide functionality that's missing at the source.
> doesn’t seem like it would be that hard technically.
It's a good idea to just keep this in mind when you're working anywhere near a production database. backup, backup, backup.
What would be the correct solution, the one that balances out both of these needs?
"Your data will be deleted in 24 hours. You can undo this deletion at any time before then."
It's 5/10/20/30 seconds for an email, which seems like a good fit for that use case. Perhaps a Facebook profile delete or mail delete might be more like a 5 min/1 hour/1 day option set, since it doesn't interfere with the primary purpose (i.e. I wouldn't want to wait an hour for all my emails to get sent, but I might be OK waiting an hour for them to actually delete).
Something similar could work here. When you delete some data, it gets soft deleted (still exists in your database/etc, but is considered deleted by the application). After a matter of minutes (hours?), it gets actually deleted. You've got a grace period in which you can undo the delete, and then it's gone forever.
Since the most common use case for an 'undo' feature is undoing something you did very recently, that should strike a good balance.
A database or IaaS application are typically used by administrators, and typically every action is rehearsed in the form of staging testing or automated tests. Furthermore, these types of applications would need significant additions or rearchitecture to account for undo operations.
For example a SQL implementation might not be able to reclaim disk space as well if it were unable to really delete the data from a dropped table. Likewise an infrastructure platform would need to keep unused resources busy. In both systems there are important ramifications of the deletion, and providing the ability to undo them doesn't exactly make sense.
As for databases, always have BEGIN TRAN at the beginning of any commands, with a commented out COMMIT.
(sorry if question is dumb, I have near zero knowledge on both HIPAA and Datomic)
I don't know why people assume those capable of building a database would also fail to anticipate this need.
The difference is that you don't use excision except in special cases (like legal obligation to delete data), you use retraction which doesn't lose any history.
> I don't know why people assume those capable of building a database would also fail to anticipate this need.
Praptak asked if it was hard to deal with and did not imply Datomic's developers failed to anticipate the need.
Excision is a feature that must be prioritized like any other and in fact was not available until Datomic 0.8.3941.
Fair point. :)
To alleviate this change for business users, we decided to build an Undo, and a Redo, for most user actions.
With a similar technique, by storing the action and it's reverse in 2 stacks. And moving them from one stack to the other.
It was quite hard to implement together with an auto-saving of the data.
But after 3 years of operations, the undo/redo is barely used. And we get requests from users who clearly didn't see it. Or don't imagine it is possible.
While I was really happy seeing it working on a web app. I'm not sure it was worth the effort, and the additional complexity to save data.
Luckily there has been work on patterns since the CoF Command/Memento: the Event Sourcing pattern (http://martinfowler.com/eaaDev/EventSourcing.html) can be used to implement system-wide undo functionality.
That said, it's really difficult for webapps to fully support undo/redo. A lot of the web is transactional, and different actions trigger follow on actions. GMail does a really good job with its undo, but there's a reason it goes away after 10, 15 seconds!
You could probably make a stronger case for improving deletes by overwriting the disk space with garbage and ensuring users that no derivatives were left around. That would be a selling point.
Imagine SnapChat with undo...
Undo is definitely harder.
The short answer is that because CouchDB uses append-only files, the B-tree root node must be rewritten every time the file is updated. However, old portions of the file will never change, so every old B-tree root, should you happen to have a pointer to it, will also point to a consistent snapshot of the database.
And I think the argument that you can't blame the users isn't fair because these are sysadmin tools - so you'd expect the users to know what they're doing.
Some database operations do have an undo command per session; up until a commit is made. But it makes no sense to have an undo for drop database, which is why it auto-commits.
Sometimes the "undo" command needs to run a layer or two lower down in the stack than on the application layer. 9/10 times, there is an undo command (assuming the platform is installed correctly) - and as the author noted, he had a backups so that would have been his "undo".
So while I'm all for building safety nets and better tools; I also think the OP is being completely unreasonable expecting every sysadmin application to have an inbuilt "undo" command. Sometimes "undo" tools need to be worked into another layer of software lower down the stack - but just because they don't exist in the GUI frontend of choice, it doesn't mean that they don't exist at all.
But that's just my opinion as someone who also constantly makes mistakes hehehe
I think we can fix that! You're right that some things aren't amenable to a literal "Ctrl Z"---but I think Sacha's underlying point is spot on.
Many "irrevocable" actions could, with better design, be made made safe.
For example, Sacha's second story was where he accidentally deleted a production server instance. With a typical VPS, his configuration might be irrevocably gone, and he has to manually recreate everything under time pressure. But if next time he uses, for example, a Docker host, then such an error will be as simple to fix as deploying the same image again.
Another good example is Git. With most version control systems, "rebasing" is either unsupported or discouraged, and if you mess up, you can lose data permanently. Git's design is undo-friendly from the ground up: any commit hash encodes the exact state of your repo, including history all the way back to your first commit. So even if you rebase or force push and make a terrible mistake, you can nearly always use `git reflog` and get things back just as they were. Also, unlike some lesser VCSs, Git is transactional---so if you say "git pull" and then trip over the power cord while it's updating, your repo will still be in a good state afterward.
Technology that has a simple, explicit concept of state--such as Docker images or Git commits--is fundamentally nicer to work with. You can explore and experiment, and if things go wrong, you can roll back with confidence.
The docker example is a great one though. Lately I've been doing a lot of work with FreeBSD and using Jails (which, if you weren't already aware, are OS level containers for FreeBSD). And using ZFS to snapshot the Jail. I've worked with virtual machines before, but I just love the ability to take snapshots of an environment before making potentially dangerous changes.
It's probably also worth noting that some of authors examples also demonstrate why you should be using root access for every day operations.
The thing is, I do relate to the point he's making, but it also feels a little like he's blaming application developers for his own mistakes (using root access for general usage, not backing up config files nor snapshots, etc). So while I think it's great that tools are getting better and easier to use, we also need to be careful not to depend on these things to save us from our own laziness. Because that level of complacency will lead to more mistakes than if the tools required our greater concentration (as the adage goes: "the more idiot proof you make something, the bigger the idiot that comes along")
This is too OO for my taste. Having just seen a talk about MiniKanren, I wonder if you can implement undo effectively with a relational (logic) programming system. Is anyone aware of research (or practice) to that effect? My googling isn't turning up anything relevant.
As a pattern it is proven to work. It just consumes memory and servers might not scale as well as a desktop app in this regard.
If the undo/redo stacks don't grow too long and the operations are simple enough in nature it might work though.
UNDO UNDO UNDO