User experience (UX) matters. A lot.
It’s not something that only user interface developers have to think about because, one way or another, we are all user interface developers (though we usually don’t think about it).
A user can be anyone, from your teammates using a new class you created, some guy in China that just downloaded the latest version of your library, or even yourself, 5 months from now, trying to understand what you meant with that extra ‘else if’ block.
I want to focus today in the UX of your code public Apis, and how to improve it a bit by failing fast.
Failing fast means to immediately stop execution and report back, usually via exceptions, when you encounter a condition that’s likely to lead to failure. Note that ‘likely’ here denotes some subjectivity.
Java Map
is a good example of an Api that doesn’t fail fast. Not only does it allow the program to keep running when there’s likely to be a failure soon, but also informs the failure poorly.
Here’s an example (with the resulting stack trace):
The problems with this UX:
Map
allows the program to keep running although it’s eventually going to fail.
The line that fails is not the same one where the error is introduced. This leads to lengthy and inaccurate stack traces (the line that introduced the error may not be even present on the stack trace).
The error message is not very informative. (What’s a null pointer and why should I even care? There are no “pointer” things in my code!)
Of course you already know what a null pointer is, that Map
(incorrectly) returns null if you ask for a key that’s not there, and that calling any method on null actually throws a java.lang.NullPointerException
. So what is the problem actually?
The problem is, your code is not nearly as popular as java.util.Map
.
Your users will not know your Api, they won’t read the documentation, tutorials, wikipages or any other shit that you’ve put up out there.
If you need to make a statement about your Api, do it in the source code.
Let’s create a modified version of Map
that fails fast:
So much better. This code improves 2 aspects of our code:
The line that fails is the one that introduces the error into the program. We’re failing fast here.
If the user wants to handle non-existent keys, it has to catch an exception. This separates the normal flow from the exceptional flow. In the first example they were the same thing.
This is great but we can do even more for our users. In this simplified example, the error is evident. But it might get tricky in a real world scenario, which leads us to…
The previous error message just says “What you want is not here. Sucks to be you”.
Error messages are one of the best ways to communicate with your users. They won’t read through your docs but they will sure as hell read a stack trace error message they got when they wanted to see the dancing bunnies.
With this in mind, let’s improve our code:
Now the user gets an error message that points exactly to the problem. The end user experience has improved a lot.
Let’s see both error messages again:
Forget about Map
for a second. If you were using a new library, which error message would you prefer to get? Which one would be more helpful while trying to find a solution?
Bottom line: Learn who your users are, and create a good Api for them. User experience matters. A lot.