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.
Digression
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.
Hey Seb, nice article, and given your two choices (error message vs carry on as normal) I’d make the same decision as you.
But I don’t think those are the only choices.
I know nothing about your domain, but given your owner->dog example, there is a case for a third option: report that there’s no change in state, but don’t make it an error.
The problem with simply ignoring the action is that the user may have to worry about an extra dimension to the model that you’re not considering – change over time. Now the database, or structure, or whatever’s storing the owner->dog relationship only has to think of “what are the relationships now?”, but your user, by virtue of being a user, has to think in terms of “this is the state before I click this button, that will be the state after I click it”.
The choice of simply carrying on as usual could give the wrong impression that the state before was actually different, after all the system gave no indication the model was already like that. How important this distinction is depends on your domain, but it’s not hard to dream up an example where it could have negative consequences. For instance, the user *accidentally* selecting the current owner when they intended to select a different one. Given no indication that they *may* have made a mistake, they could continue on their merry way, under the misguided belief they made the change they intended, and it was indeed a change.
I would argue that an unobtrusive, non-modal message that alerts the user that there has been no change is a better solution than either an error message or ignoring it.
I think the real question is, why are the error messages so onerous that you had to have this discussion at all? Error dialogues don’t need to force a mouse click – the Escape or Return key should work just as well. Better yet, make it non-modal so that you can report mistakes without requiring any extra actions.
But if you can be absolutely sure that the perception of change is no problem in your domain, then yeah, just carry on and let the user get on with their day.
P.S. Sorry for the long comment, I didn’t have time to write a short one 😉