Keeping the goal in sight while designing component flows

by Ryan Singer (@rjs)

This article was originally published to in August 2012.

The other day I wanted to sign in to a video site to watch a lecture by Clayton Christensen. The bumpy experience I had provided a good lesson in designing user flows.

The site requires you to authenticate in order to watch any of the videos they host. The brand looked familiar and I thought I might already have an account. I tried to sign in with my usual username and password combinations, but they didn’t work. Then I tried to create a new account to get access to the site. When I filled out the new account form, the site gave me an error: “That email address is already in use. Please enter a different email address.”

It turns out I did have an account already, but now I was stuck in an error state on the account creation form. I was highly motivated to see the video, so I mucked around with the back button, found the authentication screen again, looked for a “forgot password?” function, waited for the email and eventually got it. I doubt a high percentage of users would have gone through such a hassle to follow a video recommendation on Twitter.

This is an interesting case because it shows how user goals can be in conflict with the implicit goals of an implementation. The app assumed my goal was to create a new account. When I didn’t meet the criteria for new accounts (a unique email address) I was told I couldn’t proceed. But in reality I didn’t care about creating a new account. I was just trying to get in so I could watch Clay’s lecture video.

This is a product design topic. What is the user trying to do, and how can you help them do it?

The product brought me to a dead-end on the create account form because it assumed that the only thing that mattered was creating an account. The error state was narrowly limited to account creation while my actual goal was to get into the site and watch the video.

When I run into problems like these, I ask myself: how did this happen? Could I be making a similar mistake in my own apps? Instead of a clumsy design, I view it as an opportunity to learn something.

This kind of mistake is one that we make all the time. It happens when we focus too much on getting the components right and forget about what the user is really trying to do in the first place.

It’s easy to see how it happens. Someone on the business side decides the site should require people to create accounts to see the content. Therefore “user authentication” becomes a requirement on the designers’ and developers’ plates. Everybody knows how to do user authentication. You need a few key screens: new account, log in, recover password, etc. Somebody with a manager hat divides the work. New-account is a scope. Log-in is a scope. Recover-password is a scope.

Now it’s somebody’s job to implement the new-account scope. What are the boundaries of this chunk of work? Everybody knows, let’s say, that to create a new account you need a new account form, error states, a success state, and an email notification. In addition you have requirements about what kind of information you need to collect and whether things like non-unique email addresses are allowed. Given this set of boundaries and requirements, the person responsible for the new account process builds a solution.

Their solution looks like this:

The account creation process in flow shorthand. States are above the bar, actions are below the bar.

It covers all the cases for account creation and it perfectly respects the requirements. Somebody with a manager hat agrees that it checks all the boxes, and the team moves on.

What could be wrong with this? Actually this is all a nice process. The problem happened in the very beginning. As soon as a team breaks a problem like user authentication into component pieces — like create account, sign in, recover password — it easily starts to mistake those pieces of implementation with user goals. People tasked with a component forget that nobody cares about creating an account just for the sake of the account. They start imagining a user driven to create an account. Real users live in a larger context. They’re trying to do something more interesting like watch a Clayton Christensen video.

That’s why it’s important for product people not to fall into the trap of designing and reviewing components in isolation. Yes you need to divide a problem into component pieces, build the pieces, and evaluate them individually. But you don’t stop there. After you divide and conquer you have to reintegrate and review.

How do we integrate the components back into a context for review? Ask the question: “What is the user trying to do here?” The job the user has in mind is the best integration point because the user’s mind doesn’t tidily follow the boundaries of implementation.

Let’s apply this to the example problem. Say we have a fixed requirement that all users on the video site must have a unique email address. What do we do if somebody tries to create an account with an email address that is already taken?

From the perspective of the component alone, the goal is to make new accounts per the validation rules. A programmer will naturally see a non-unique email as an impediment to creating an account and interpret the resulting state as failure. From there it’s logical to respond to the user with an error. “Sorry, it didn’t work.”

From the user’s standpoint, the job is different. They are trying to watch a video. They are only creating an account because it seems necessary to reach their goal. From this perspective, if their email address is already in use, this is good news. They already have an account! We can offer them a link to confirm via email that they own the email address. When they click a verification link in the email we can immediately take them to the video they were trying to watch in the first place. No detective work or back buttons necessary. That’s a better design.

The improved account creation process with an exception for existing email addresses in flow shorthand. States are above the bar, actions are below the bar.

This shows how the same process can be designed in two different ways depending on whether your view is confined to the edges of components or widened to the scope of the user’s job. Insights like these come when we step back from the individual components and look at the larger process in which the components participate.

This is not an argument against divide-and-conquer. We still need that. This is a reminder that whatever component we build, we should step back again and again and ask ourselves the question: What is the user really trying to do here?

Show your support

Clapping shows how much you appreciated Ryan Singer’s story.