Hacker News new | past | comments | ask | show | jobs | submit login

Making an action undoable often more than doubles the development cost of that feature. Google can do it, for a small startup it only makes sense in a few situations.



It is particularly difficult in a collaborative application with 'hard' links between records. For example, consider this scenario:

(1) Sally deletes an employee record

(2) Bob deletes that employee's department

(3) Sally tries to undo the deletion of the employee - will the employee be restored without a department?


>(1) Sally deletes an employee record >(2) Bob deletes that employee's department

I would argue that a good design would actually prevent you from deleting important records like these. i.e. when Bob clicks delete, it just marks the object as deleted and it stops showing up in reports/interfaces. Then maybe you have something clean it up 90 days later or something.


Yes. This is pattern I've seen in both companies I worked for, and it makes so much sense.

Similairly you don't make table ARTICLES with column AMOUNT and KIND. You make table CHANGES with DIFFERENCE and KIND, and only ever add rows to that table (possibly with negative DIFFERENCE).


Sorry I don’t really understand what you’re trying to say here. Could you expand on the differences in data between the two tables, please?


In the first case, you only know the AMOUNT. You can't undo transactions because you only have 1 object stored, the total amount. If you store DIFFERENCE, then you'll have a table of every transaction and can undo them individually. The cost is having to sum the table whenever you want to know the amount (which is usually worth the flexibility).


Regarding the performance - you can add triggers writing to additional table with a sum, or maintain that table in the code that adds rows, or calculate it each time you want to know the sum.


This is what I understood (s)he means

Scenario: Laptop's price is initially $800 and then increases to $850 before dropping to $825

Design 1: with columns Amount and Kind will just have a single row which will get modified with the latest value

Design 2: Will have 3 rows in the above case

Difference | Kind | Time stamp

800 | 'Laptop' | ts 1

+50 | 'Laptop' | ts 2

-25 | 'Laptop' | ts 3

So this latter design has 'undo' builtin

edit: formating


Yes, that's what I've meant. This also gives you easy accountability (rows can have information like user_id, ip, timestamp).


Intuitively, I would expect it to restore both the department and employee, or throw it into a "Bob and Sally: Please fix this conflict" kind of box.


I'd expect it to depend on the permissions of the user restoring or deleting the groups.

Under complete presumption: Bob is an HR member while Sally is a Manager. Why? The ability to manage entire groups should be left to people higher up in the administration chain where individual users should be delegated to HR or other staff.

Bob should have permissions to manage users but not groups. Sally should have permissions to manage both. If Bob deletes a user, then Sally deletes the group, there should be no reason that Bob should be able to anything outside his domain. The group should not be restored and Bob should be given a clear, 'plain English' reason why he can't do such an action.

If both Sally and Bob are managers of a group and said situation occurs, no group should be restored when Bob restores a user. Only a readable error message should occur. In fact no data to be restored should be there in the first place unless it has been backed up or cached.

What is a possible solution to this logic?

1.) You could implement an optional backup system. All deletes are final in the context of the action. Think of a common tree structure: if no main branch is there to support a twig then no twig can hang. No restores via users can be performed until the group has been restored. The deleted data could be stored in a cache until certain parameters are met; once these are met the data is deleted (IE: Recycle Bin)

2.) Users could be restored belonging to a default group. If a recently deleted member of a recently deleted group needs to be restored, they could be assigned to a default 'holding' group or other similar group with restricted permissions. This retains all specific user data which is not lost and by default the user will not be given extraneous or extra permissions. They can then be assigned to an appropriate group.

3.) Removing a group does not affect users within it. This is the opposite of a tree structure. This is analogous to Linux group and user permissions. A group can be assigned to users so they can access a resource. They are not interconnected in any way other than their usage to control permissions. Users can be removed and restored at will and so can groups. No conflicts of hierarchy are incurred.

In summary, restoring an individual resource should never restore the controlling body without given consent. This would create a lot of problems if it did.


Yes, it gets complex fast in collaborative setting. To my understanding, there are two robust approaches to collaborative editing:

- git-style 3-way-merge

- Google Wave -style operational transform

If somebody is experienced on the topic, I'd love to learn more about different approaches. Also, it would be awesome to have these as an open source DB projects (well, we have git) or even offered as IaaS.


(3a) No, because your data model never allows physical deletes. (3b) And because you modeled up to 6NF with temporal keys, you've got the record from (1) and (2) and you know at what times they were related.


If Bob truly has the authority to decide that the entire department can be deleted from the database, and if the employee record really does have a dependency on the department, then the correct response is either: a) Sally's interface has already updated to reflect the fact that the department has been deleted, so she doesn't even have the option to delete anymore b)(where realtime updating of the interface isn't possible) - when Sally tries to modify the deleted record she is presented with a message stating that the action failed due to the department having been deleted by Bob. Sally can now either undo Bob's delete first (or ask Bob to do it), and then undo her own change.

User confirmation dialogues are a UI smell, in the same way that a module of code stuffed with if statements and flags is a code smell. It's telling you that your design has a fundamental flaw that needs to be fixed in a non-trivial way. Just t hink about the basic question - should red be "delete" or "no". Either way, the question implicitly recognises that this situation is confusing, and that Sally has a good chance of making a mistake. Knowing that her problem has arisen because she clicked on the wrong button ("when it was clearly coloured red!") is not going to make Sally like your software. Make it so that she can fix her error, not so that she doesn't make the error in the first place.


I 100% agree `Undo` is the best way to go about this. Equally, I'm very curious to hear how others have implemented this in their app. The complexity for such a small feature is actually quite high, because we start playing with the database (and therefore, in many cases a robust ORM)

Anyone have experience with doing something like this in Rails?


We solve it using an "archive" collection in mongodb. Deleting moves a document from one collection to the archive, while undo moves it back.

We also implement ability to find deleted documents by id, by searching the archive if the record is not found in the original collection.

API looks like this:

    // delete something
    delete("/products/123")
    
    // see it in the archive
    get("/archive/products/123")
    
    // undelete it
    put("/products/123", {$restore: true})


One simple implementation is just to queue up the action without committing it, then "undo" actually just cancels a pending action. The downside of this approach is "undo" becomes a limited time only offer.


I just stick an isDeleted column on my tables. It does mean I have to remember to do a `where !isDeleted` on all my main queries, but apart from that the cost of undoing is incredibly cheap.


Actual deletion occurs on app close (or next open). The "delete" the user sees just hides the data.


But you can still mark the buttons with colors so your users will make less mistakes. I think that the two issues are orthogonal.


It doesn't - the undo action is not a permanent option since it is only a replacement for a yes/no prompt. The implementation then could be a delayed action. Might not work for all use cases, but good enough for most, especially in a start up situation.


Saving dev costs by making your users pay with their data loss is extraordinarily short sighted. If you cant code with an undo feature, perhaps this is not the discipline for you.


How many situations does the small startup have where they're going to be destroying something?


When your startup allows users to create categories or unit groupings, maybe?

Removing credit cards, as well.

I dunno, do you really think it's rare for startups to be deleting data?


I think they delete data all the time. But I think there are just a small number of situations where it happens.

For example, someone on HN probably deletes a comment every few seconds. The site is therefore deleting data constantly. But all that only counts as one single situation.

Is there any other situation where an HN non-admin ever deletes data?


That's a good point; I don't think I can delete my submissions or my account on here, but even then 3 total use cases revolving around delete isn't exactly a huge number.


CRU


> Making an action undoable often more than doubles the development cost of that feature.

Every time I've added the feature, it's bordered on trivial (start a timer to execute the actual delete request, show a link to cancel the request, hide the DOM element).

I suppose if you were doing all the work server-side, then yah, it would be difficult. But the way GOOG does it (IIRC) it's not that hard at all.


That is cancellation then, not undo.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: