We recently had an interesting discussion at work about our UI paradigms.
Let’s assume we have an information management system which allows you to create, manipulate and link objects which represent, say, Persons and Dogs.
Now assume that through a wizard, we allow a user to link a Person to a Dog as an “Owner”.
Lastly, let’s imagine a scenario in which the Person is already linked to a Dog, but the user runs through the wizard and selects the Person as an Owner anyway. Here’s the crux: inherently, our system allows multiple links between objects. But in this case, our business rules dictate that we don’t want multiple links with the same link reason (because why duplicate the same link? We’re not adding any new information)
So what do we do? We have two options:
Option 1: Bad user! Bad!
The objects shouldn’t be linked more than once, so the user is attempting an illegal operation. Show an error message telling them what they’re attempting is not possible.
Option 2: Uh, everything’s fine!
The target state of our objects is the same as the current state, so do nothing and act as if the operation has succeeded.
I understand the allure of option 1 – it’s a hardliner’s approach to dealing with illegal operations. The user is not only attempting to do something which is not logically allowed (linking twice as an Owner would add no further information to the system), but has already been done. We’re keeping things as simple as possible for ourselves. But in my mind it’s just too hard line. Yes the user’s being a bit daft but why punish them so harshly – they now have to click OK on the error dialog and close the wizard. Two extra clicks and no change in state.
On the other hand, what I proposed was an idempotent approach to our linking mechanism.
Idempotent operations are functions which can be applied many times over, but which will only make a change the first time. For example, multiplying by zero. If we take a number (say, 1) and multiply it by zero, we get zero. If we then multiply the result by zero again, we still have zero. Multiplying by 2 isn’t idempotent because we would be exponentially increasing our result – it would go from 1, to 2, to 4, to 8, to 16. And our result would keep changing.
In essence then, if a user selected a Person to be linked as an Owner, and they were already linked, we would perform no additional functionality but behave as though the operation had succeeded. The result is the same no matter how many times you link the Person as an Owner.
Some of my colleagues disagreed, and argued that we should never be silently suppressing errors. I agree. But in this case I don’t think we’re suppressing anything; we’re simply choosing not to define what the user is doing as invalid.
Furthermore, let’s think about this in terms of what a user would expect from our system. If they’re trying to do something which will only leave our system in the same state, why slap their wrist?
You’re not punished for trying to turn a light switch off again, or pressing the lock button on your car-key a second time, or double-clicking a link on a website. Because the results are the same no matter how many times you apply the operation.