GSoC 2017.2/Take us to your master

User Experience. The word evokes this existential dread in the mind of many developers.

But… Who are we writing for? Who is going to use the software that we are writing on a daily basis, weekly basis?

The stated audience for LibraryBox and PirateBox are opposites of a spectrum: LibraryBox was built for people who aren’t necessarily technically minded, while PirateBox is definitely more intended for those who are comfortable with fiddling around in the command line and who are willing to put more energy into understanding the system that they’re using than the cursory “how do I work this crazy thing?”

How a checkbox can make all the difference in the world.

In Alexandria, the next generation base daemon and service that I’ve been writing, I’m including a checkbox: Expert Mode. Expert Mode does one simple thing: It lets the web configuration environment do advanced things. At the moment, it doesn’t do much; However, this is what an Expert Mode page might look:

Hic Sunc Dracones.

So that covers the “power user” side of the equation. What about the non power user situation? In the case of a non-power user, we need to make choices for them.

The screenshot above is for the expert version of the Storage configuration; indeed, this is the part of the settings that haven’t even been hammered down yet. This brings me to my next point:

We make choices for our users

The systems that we build are built upon the architectural choices that we make, but how we interact with these systems is important. One of the fellow developers I know, John “Warthog9” Hawley (yes, that John Hawley, for those in the Linux community) said to me at one point that most of what he builds isn’t intended for “human consumption” — that is, that his code is mostly for him and not anyone else. This means that he can make choices for himself and not have to worry about the consequences; so what happens when we build code that is for human consumption?

We have to make choices on behalf of the users.

Most of the time, these choices are no-brainers: How do we name a button (“Save” and “Cancel” are good options when those are the best descriptions we have for them) and how do we handle failures (gracefully, in all hopefulness). These choices, in the end, are what make up the overall “user experience” that we build for our users.

So, let’s look at one choice I’ve had to work through.

Internal, External: The choice is (possibly) yours

The original PirateBox didn’t care where your files were stored so long as there was a decent chance that there are files there to be read. That was really all it cared about. As it moved to the OpenWRT based world, it needed to have some amount of “just put a flash drive and mount it”. This was fine as there was no storage in the environments that OpenWRT exists in. There is something else however when you get to systems with gigabytes of disk space such as the Pi and other network devices.

The Pi is often blessed with a large disk to work on with the primary storage being a microSD card. This microSD card is likely going to be much larger than the storage space of any router — in the several gigabytes as opposed to the several hundred megabytes.

This raises a question: Is it okay to use this storage for content? The immediate answer is “Yes”. This answer is fine until you get into the foggy question of “Well what about if you have a USB disk inserted?”

These are two pages out of my notebook. There’s a lot in my notebook.

The immediate answer was that the internal disk should be used if there is no external storage was available; this lead to a violation of the Principle of Least Surprise. The process looks like this:

  • User sets up the software
  • User adds some content via the upload function
  • User inserts a USB disk
  • Suddenly, content is “gone” that was there before.
  • User is unhappy: content is gone.

This lead me down the path of “Well why not have multiple storage options?” This meant that I could have many different storage options in the end. Let’s walk through how that works:

  • User sets up software
  • User adds some content
  • User adds a USB disk; we name it “External Library” (this is in of itself confusing, but that’s another thing to tackle)
  • Content is there, and we can add more content.
  • User adds another disk
  • Content is still there. All is fine. But… How do we name it? We give the disk “external library 2” as a name. User can figure that one out.
  • User removes the first USB disk
  • Second disk is still called “External Library 2”

While not confusing, the problem still arises: what happens when only the second disk is inserted? When the device is next rebooted, there won’t be any good way to recall (without writing and reading information from disk) that it was called “external 2” — then what happens when we end up with two “External 2” disks? What happens when we end up with multiple disks that are named wrong?

So we fall back on the original behavior: Require a disk. This is familiar to the previous users (they’ve been made to expect that they need a disk) but also a reasonable question to ask. What about using the rest of that disk? That’s where a simple checkbox can make all the difference. In “Normal” mode, we can ask “Internal or External”. In “Expert” mode we can say “Use this specific disk for content and pass these specific options to mount”.

This leads to a new pair of user interactions under default situations:

  • When the user first tries to browse or upload, there must be something plugged in (so that there can be a filesystem to write to). If there is not, the user is alerted that there’s no disk that can be used currently to present content.
  • If the user feels it’s important to have internal storage used, they can turn it on (using the configuration panel) with the knowledge that it won’t show the content on an external disk

If the user wants something else, they can configure that themselves. That’s why we have expert mode: You need something outside the normal state of existing and now you get it.

Wrapping up

So, now we’ve covered how one little interaction has a lot of things going into it. How can we generalize? My takeaway is that:

  • It’s better to list out the assumptions we make about the user, our environment, etc.
  • Make a sane choice
  • Warn when making a choice might have unexpected consequences/Explain the ramifications of a user’s actions.

A take away that I can’t stress enough in building systems however is: You are not the user. Thinking like a user is a little harder than you might think. The problem-solving abilities that we take for granted only show up in the top say, 20 percent of users, so be clear on what you want.