As with many of my blog posts, it is often a tweet or another blog post that made me want to write my thoughts. In this case it is both:
Just accidentally blew away 16 files' & days' worth of work with hg. Looked up, TIme Machine had just finished. Lost < 1 min. of work.
That tweet prompted Wolf Rentzsch to write this post about how Time Machine is your version control "safety net". To me both of these are shocking. It seems that the tweeted mishap may have been user error, but it made me look into how the various user interfaces help protect the user.
For a long while I was opposed to VCSs, mostly due to faffing around with subversion and getting nowhere. With DVCSs I've become an addict. The issue is that DVCSs also try to be clever. Their primary goal is to protect you from yourself, to make sure your code and its history are safe. Essentially your commit history should be like a stack. 99% of the time you push onto the stack, rarely you need to pop off a stack. You can read the contents of the stack as you want. But you shouldn't be able to modify the internals of the stack.
This is why I consider the many rebase plugins/commands in DVCSs to be harmful. I feel that rebase should be viewed as a mixture of goto, premature optimisation and Jeremy Kyle all rolled into one ie don't touch it unless you know what you're doing and are wearing many layers of protection, and even then it probably isn't such a good idea. Some people swear by rebase, but I feel that you shouldn't be messing with what should be treated as sacred.
This is where we get onto UI. I've never really given much thought to command line UIs, but comparing git, Mercurial and Bazaar I've found subtle differences in the UI that make it harder or easier for a user to perform a destructive action. These really fall into 3 categories: confirmations, making it hard to destroy and separating destructive actions.
In the case of the tweet at the start, I looked into the command that was performed:
hg update -C . It offers no confirmation dialogue, it just cleans out the items that have changed. It doesn't make it hard to destroy changes and it is a one character option which means it is easy to do. I'm not sure about git but the equivalent in Bazaar would be
bzr revert --no-backup. Of course there's no real reason to run --no-backup, it helps save your arse just in case and cleaning it up is just a case of
bzr clean-tree --detritus (which lists the files to be deleted and asks for a confirmation).
One of the other examples is from the blog post.
git branch -d abranch will delete the branch called abranch. Now to me this seems incredibly dangerous as there isn't any confirmation given. Occasionally I've seen warnings to use the uppercase -D option if changes aren't merged into the parent, but sometimes even those may not appear. If you're doing something like deleting a branch then it should be made harder to do. Really it should be
git branch --delete-branch abranch and then ask you to confirm the delete.
Destructive actions should really be moved out into other commands if possible. bzr has 2 such commands: uncommit and clean-tree. Uncommit and clean-tree both list the items that will be removed and ask you to confirm. This makes the user double check what they're doing to confirm what they are doing. Now these commands could be moved into other commands. Uncommit could be made into
bzr commit -d, but by separating them out you are making the user think that this is a different thing and making it harder for them.
It does seem very hypocritical, when most of UI design is a push to make life easier for a user, to advocate making it harder. But as odd as it may seem, making something harder may make it more usable. Now by harder I don't mean make a series of convoluted steps that require an animal sacrifice. I mean add an extra step to any potentially dangerous action. Tell the user exactly what the result of the action will be and ask them to confirm it.
Obviously you should provide undo when possible, but sometimes it isn't. Think about where in your UI you place these actions and consider whether you need them there at all. Don't put a 'wipe library' button right next to one of the most commonly used buttons in your UI, and no matter where you put it, if you can't offer an undo then tell the user exactly what you will do and make them confirm. Force them to pay attention to what they are doing. They'll thank you for it, even if it is just by not complaining to you (or worse to the internet) about how your software lost their data without warning.
At NSConference Dave Dribin made the following statement in a slide:
Delegates are preferable to Notifications which are preferable to KVO
Now this caused a bit of a murmur on Twitter, as it was taken as "KVO is bad". As this is an important topic and not one that can easily fit into 140 characters I thought I would write a post to outline my rules for when to use each technology and why use them where I do.
One of the main reasons delegates were listed as the most preferable is that they provide the least 'magic' way of communicating between objects while keeping them loosely coupled. It sends the message directly to the object and you can see what method is being run. As such it makes your code more self explanatory. So whenever you have a 1 to 1 relationship between two classes, opt for delegation over notification.
Notifications should really be used wherever you would use a delegate, but need to communicate with any number of objects. They should really be thought of as 1 to many delegates. Quite often in Cocoa you will see a case where there is a notification and a delegate method for the same event (eg change of selection in a table view). These are cases where 80% of the time you just need a 1 to 1 relationship, but occasionally do need a 1 to many relationship. You will rarely encounter this in your own code, unless you are building a class for use in multiple apps.
This is where I have found KVO to be the most powerful. You need to keep various UI elements in sync with your data model. Changing the value of the model from one view should also update the value in other views. KVO was built for exactly this situation and as such requires much less code than delegation and notifications.
To achieve the same effect with delegation or notifications you would have to add delegate methods or post a notification in each setter method of each model object. This obviously is a lot of code that needs to be written. Code that could have bugs and code that makes your source more cluttered. KVO of a value in an object requires just one line of code and the implementation of one method. And if you use bindings then it is actually no code at all.
I'll start with the exception to this: if you have multiple callback options then use delegates (eg NSURLConnection). But the vast majority of asynchronous operations need just a single callback. In these cases, if you are using Mac OS X 10.6, then you should be using blocks instead of delegates. Blocks reduce the layer of indirection in your source by putting the call back information inline with the invocation of the method.
The next two rules aren't quite rules of communication, but are relevant to the discussion at hand.
This should be pretty obvious. If something in your code isn't explicitly clear from the code itself then comment it. If you are sending off a notification say why you are sending it off, so you can get an idea of what it is doing elsewhere in the application.
There is no such thing as magic in programming, simply things we don't fully understand. If notifications, delegation, KVO or anything else seems like magic, then read up the documentation on it or ask a developer experienced in that area to explain how it works. When you find out how they work you realise that what seems like magic is fundamentally a very simple process.
Ultimately, what Dave said was right for some scenarios. Each technology has its use case and you should only use it where it is needed. You should always aim for the smallest amount of indirection in your code while still retaining loose coupling of classes. KVO and notifications are necessary in some occasions (unless you want to go crazy with delegation) but they should only be used when they are needed.