A Case Study In Design Tradeoffs: Usability vs Discoverability

Usability and Discoverability (also referred to as Learnability) are often confused with one another, but they really are distinct concepts. In Joel Spolsky’s wonderful User Interface Design for Programmers (go read it!), Joel provides an metaphor to highlight the difference.

It takes several weeks to learn how to drive a car. For the first few hours behind the wheel, the average teenager will swerve around like crazy. They will pitch, weave, lurch, and sway. If the car has a stick shift they will stall the engine in the middle of busy intersections in a truly terrifying fashion.
If you did a usability test of cars, you would be forced to conclude that they are simply unusable.

Scary Driver

This is a crucial distinction. When you sit somebody down in a typical usability test, you’re really testing how learnable your interface is, not how usable it is. Learnability is important, but it’s not everything. Learnable user interfaces may be extremely cumbersome to experienced users. If you make people walk through a fifteen-step wizard to print, people will be pleased the first time, less pleased the second time, and downright ornery by the fifth time they go through your rigamarole.

Sometimes all you care about is learnability: for example, if you expect to have only occasional users. An information kiosk at a tourist attraction is a good example; almost everybody who uses your interface will use it exactly once, so learnability is much more important than usability.

Rick Osborne in his post, Usability vs Discoverability, also covers this distinction, while Scott Berkun points out in his post on The Myth of Discoverability that you can’t have everything be discoverable.

These are all exmaples of the principle that there is no such thing as a perfect design. Design always consists of trade-offs.

Let’s look at an example using a specific feature of ASP.NET Routing that illustrates this trade-off. One of the things you can do with routes is specify constraints for the various URL parameters via the Constraints property of the Route class.

The type of this property is RouteValueDictionary which contains string keys mapped to object values. Note that by having the values of this dictionary be of type object, the value type isn’t very descriptive of what the value should be. This hurts learnability, but let’s dig into why we did it this way.

One of the ways you can specify the value of a constraint is via a regular expression string like so:

Route route = new Route("{foo}/{bar}", new MyRouteHandler());
route.Constraints = 
  new RouteValueDictionary {{"foo", "abc.*"}, {"bar", "\w{4}"}};
RouteTable.Routes.Add(route);

This route specifies that the foo segment of the URL must start with “abc” and that the bar segment must be four characters long. Pretty dumb, yeah, but it’s just an example to get the point across.

We figure that in 99.9% of the cases, developers will use regular expression constraints. However, there are several cases we identified in which a regular expression string isn’t really appropriate, such as constraining the HTTP Method. We could have hard coded the special case, which we originally did, but decided to make this extensible because more cases started cropping up that were difficult to handle. This is when we introduced the IRouteConstraint interface.

At this point, we had a decision to make. We could have changed the the type of the Constraints property to something where the values are of type IRouteConstraint rather than object in order to aid discoverability. Doing this would require that we then implement and include a RegexConstraint along with an HttpMethodConstraint.

Thus the above code would look like:

Route route = new Route("{foo}/{bar}", new MyRouteHandler());
route.Constraints = 
  new RouteConstraintDictionary {{"foo", new RegexConstraint("abc.*")}, 
    {"bar", new RegexConstraint("\w{4}")}};
RouteTable.Routes.Add(route);

That’s definitely more discoverable, but at the cost of usability in the general case (note that I didn’t even include other properties of a route you would typically configure). For most users, who stick to simple regular expression constraints, we’ve just made the API more cumbersome to use.

It would’ve been really cool if we could monkey patch an implicit conversion from string to RegexConstraint as that would have made this much more usable. Unfortunately, that’s not an option.

So we made the call to favor usability in this one case at the expense of discoverability, and added the bit of hidden magic that if the value of an item in the constraints dictionary is a string, we treat it as a regular expression. But if the value is an instance of a type that implements IRouteConstraint, we’d call the Match method on it.

It’s not quite as discoverable the first time, but after you do it once, you’ll never forget it and it’s much easier to use every other time you use it.

Making Routing with MVC More Usable

Keep in mind that Routing is a separate feature from ASP.NET MVC. So what I’ve covered applies specifically to Routing.

When we looked at how Routing was used in MVC, we realized we had room for improving the usability. Pretty much every time you define a route, the route handler you’ll use is MvcRouteHandler it was odd to require users to always specify that for every route. Not only that, but once you got used to routing, you’d like a shorthand for defining defaults and constraints without having to go through the full collection initializer syntax for RouteValueDictionary.

This is when we created the set of MapRoute extension methods specific to ASP.NET MVC to provide a façade for defining routes. Note that if you prefer the more explicit approach, we did not remove the RouteCollection’s Add method. We merely layered on the MapRoute extensions to RouteCollection to make defining routes simpler. Again, a trade-off in that the arguments to the MapRoute methods are not as discoverable as using the explicit approach, but they are usable once you understand how they work.

Addressing Criticisms

We spent a lot of time thinking about these design decisions and trade-offs, but it goes without saying that it will invite criticisms. Fortunately, part of my job description is to have a thick skin. ;)

In part, by favoring usability in this case, we’ve added a bit of friction for those who are just starting out with ASP.NET MVC, just like in Joel’s example of the teenager learning to drive. However, after multiple uses, it becomes second nature, which to me signifies that it is usable. Rather than a flaw in our API, I see this more as a deficiency in our documentation and Intellisense support, but we’re working on that. This is an intentional trade-off we made based on feedback from people building multiple applications.

But I understand it won’t please everyone. What would be interesting for me to hear is whether these usability enhancements work. After you struggle to define constraints the first time, was it a breeze the next time and the time after that, especially when compared to the alternative?

What others have said

Requesting Gravatar... Robb Allen Nov 06, 2008 3:25 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
I am so going to steal that picture.
Requesting Gravatar... Torkel Nov 06, 2008 5:07 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
Why not provide something like a fluent interface, that would create a more usable & discoverable API:

www.codinginstinct.com/.../...e-in-mvccontrib.html
Requesting Gravatar... Simone Nov 06, 2008 5:08 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
Seems like part of your job for the next few months will be explaining each design decision you made on the framework, as someone will always question the decision you did :)
Don't get me wrong, it's good to have critics, as they help improve the product, but sometimes someone is just too critic :)
Requesting Gravatar... josh Nov 06, 2008 5:14 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
Let's caption the picture. I''l start..

Harold and Kumar go to Wayne's World

(that might only make sense to me though)
Requesting Gravatar... Liam McLennan Nov 06, 2008 5:17 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
"we’ve added a bit of friction for those who are just starting out and have trouble using Google"

Apart from being slightly insulting towards a respeced member of the .NET community I think this statement is incorrect. I have found Google nearly useless when it comes to Asp.Net Mvc. There is very little information out there and most of tends to be out of date. I have better luck directly searching Scott Gu / Haacked / Hanselman. I look forward to the official release so that all the books will finally ship.
Requesting Gravatar... Thomas Eyde Nov 06, 2008 6:23 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
A dumb question, couldn't you solve this with overloading?

This also remind me of the CommandArgument property, which is a string, while the event handler providing the actual value defines it as an object. It can never be anything but a string, yet we have to downcast it every time. No one bothered to explain this decision as far as I know.
Requesting Gravatar... haacked Nov 06, 2008 7:44 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
@Liam it was a joke. I've known Ayende for a long time now and he probably has a thicker skin than I do. If *he* finds it insulting, I will retract it immediately, because I like the guy. Apparently a subsequent commenter on his blog post found the answer via Google.

@Thomas Not really. Unless we broke up the constraints into two dictionaries, one that is string, string, and one that is string, IRouteConstraint. Is that what you mean?
Requesting Gravatar... haacked Nov 06, 2008 7:47 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
@Torkel looking at that page, I see this:

.WithConstraints(new { id="^[0-9]+$" })

Isn't the argument to "WithConstraints" an object? That's exactly the discoverability problem that Ayende talked about in his blog post, which I addressed in this post.
Requesting Gravatar... Ayende Rahien Nov 06, 2008 7:55 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
I didn't get the joke, I am afraid.

As for the API discoverability issue.

Change IRouteConstraint to AbstractRouteConstraint (you like to do that anyway) and define an implicit convertion operator from string to AbstractRouteConstraint.
That will let you define constraints as an enumerable AbstractRouteConstraint. so you can write it like:

new AbstractConstraint[] { @"\d4", new MyCustomConstraint()}
Requesting Gravatar... Erik van Brakel Nov 06, 2008 8:04 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
@ Ayende:
That's what I was thinking as well. I guess the API now is a result of thinking in interfaces too much, while neglecting the fact that using an abstract base class still IS a valid option in a lot of cases?
Requesting Gravatar... Ryan E Nov 06, 2008 8:44 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
Christ, when did "ease of development" in the form of terseness outweigh code clarity and intent. I'd much prefer fluent interfaces or even your second code snippet.

Your code would never make it to production in my world. I have developers of all skill levels that need to maintain my codebase, and I ensure you I'd waste resources as they tried to figure out what your code was doing.

If I have to use Google to understand your intent, you fail.
Requesting Gravatar... Neil Mosafi Nov 06, 2008 8:52 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
Firstly, why is constraints not a read only property? Is there a semantic difference between constraints being null or an empty collection?

Secondly, I don't get why you can't use overloads - one which takes an IRouteConstraint and one which takes a string. In the string version you can convert the string to an IRouteConstraint and call the other overload.
Requesting Gravatar... haacked Nov 06, 2008 8:58 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
@Ayende I sent you a personal email regarding the joke.

After all the grief I've received for defending occasional uses of Abstract Base Classes, you're suggestion is to add one! :P

Kidding aside, we've already shipped RouteValueDictionary and IRouteConstraint as part of the Framework, so we obviously can't get rid of those or change them. However, I do like the idea of adding AbstractRouteConstraint and perhaps a strongly typed overload for MapRoute.

The only problem is that really belongs in the core framework and I can't touch that right now.

Let me noodle on this a bit.
Requesting Gravatar... Andrei Rinea Nov 06, 2008 9:59 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
Offtopic as usual :P but who's on the passenger seat? It somehow resembles Jeff Atwood (that would be titled "Driving Horror" :P ), that's why I'm asking.
Requesting Gravatar... haacked Nov 06, 2008 10:17 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
@Andrei that's my younger brother. :)
Requesting Gravatar... Rob Conery Nov 06, 2008 10:25 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
I'm going to guess that was Phil's brother on their roadtrip a few years back... am I right Phil?

Ayende I have a hard time thinking you're offended by anything :) and I mean that in a nice way.

Phil maybe it's time to invent the AbstractInterface and use it for everything.
Requesting Gravatar... haacked Nov 06, 2008 10:34 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
@Rob, that's right. Here's the photo set on Flickr.

As for the insult, Ayende and I smoothed things over via private email and it's all good.

To make amends, next time he sees me, he's going to shoot me in the foot. But only the left foot, not my good foot. ;)
Requesting Gravatar... Brannon Nov 06, 2008 11:20 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
I don't understand what all the fuss is about. It took me a few seconds and one Google query the first time I needed to add a constraint. I agree with Phil in that 99% of the time I'm going to want a simple regex constraint, but I appreciate the ability to add a more complex one if necessary.

If intellisense had shown me I needed to supply an IRouteConstraint instance, it would have taken just as long to Google which classes implemented it, or worse I might have started implementing a RegexRouteConstraint myself!

Also, how would an implicit string conversion to an abstract class be any more discoverable? Everyone would just use the abstract class directly, defeating the ease of use.

Ultimately the problem is lack of documentation, which is understandable considering the code just made beta.
Requesting Gravatar... Torkel Ödegaard Nov 06, 2008 8:29 PM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
hm.. stupid me, my fluent interface for routing does not solve the discoverability aspect of constraints. But one could fix that by extending it with:

MvcRoute
.MappUrl("questions/{id}/{urlName}")
.WithRegexConstraint("id", "^[0-9]+$")
.WithRouteConstraint("urlName", new MyCustomRouteConstraint())
.ToDefaultAction(x => x.ViewQuestion(0))
.AddWithName("QuestionsById", routes);
Requesting Gravatar... Rob S Nov 06, 2008 8:56 PM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
I think Ryan E. nails it. I'd also question whether applying a user interface guideline is appropriate for an application programming interface.
Requesting Gravatar... meisinger Nov 07, 2008 1:04 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
you have to be kidding me... right?
how many routes are you guys building?
Requesting Gravatar... glompix Nov 07, 2008 1:52 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
I think the implementation as-is is absolutely perfect. It's terse and understandable. You can just look at a route and understand "oh, this state/province parameter only accepts 2 letters."

I've built three MVC apps so far, (2 of them in production, one still in early development) and have maybe had to use 2 or 3 constraints, tops. Even then, I think Hanselman's videos explained all of this a long, long time ago. It seemed completely intuitive to me, given that anonymous objects are used to define default values as well.

Great job on this routing stuff, Haack. The only qualm I have really is with IIS6, but that really has nothing to do with MVC itself.
Requesting Gravatar... dhasenan Nov 09, 2008 9:32 PM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
meisinger: It's rare that you will add a route constraint. It's okay, then, if you need to write more code than you would like, as long as it's obvious how to do this. Either well documented or using static typing as a hint.
Requesting Gravatar... Jeff Atwood Nov 10, 2008 9:05 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
This is the best photo in any blog entry, ever!
Requesting Gravatar... haacked Nov 10, 2008 9:26 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
@atwood Tell Joel that he should use it in the next edition of his usability book. :)
Requesting Gravatar... LD Nov 12, 2008 12:41 PM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
If I have a bookDetails.aspx page and I use MVC Routing to make the url friendly. But how can I construct the web.sitemap file to let my sitemappath control knows it the details page of xxx book and shows the boos's title at the end of the path
Requesting Gravatar... Dave Dec 09, 2008 7:18 AM
# re: A Case Study In Design Tradeoffs: Usability vs Discoverability
I understand that ASP.NET MVC is not ready for prime-time, but as a developer just getting into the framework now (beta), I have to express my disappointment that you're going forward with the anonymous-classes-née-JavaScript-dictionaries strategy.

Let me explain, I've done my fair share of JavaScript development using script to do the heavy-lifting in UI view developement. It has a place there. Clientside Web UI code tends to be tightly bound in a works-just-here-and-dont-mind-the-magic kind of way.

MVC and Routing are not the same. You're building this technology in an environment where companies pay real money for developers to use Visual Studio and it's spectacular Intellisense capabilities. The ability to "type three characters and press up, up, up, down, enter" is an astounding productivity boost. However, in MapRoutes(), I'm lost without spending my afternoon searching StackOverflow, your blog, other people's blogs about older preview versions, source code, and Red Gate's Reflector before I can understand sorta-kinda what I'm supposed to pass and how it will interact with other routes for an even slightly complex case (e.g., "/foo/1/bar/2/baz").

I hope that you seriously reconsider this dynamic-language-work-alike hack (in the clever hacker sense of the word) before releasing publicly. Yes, it's a neat use of the language. Yes, one-liner's are sweet. But, No, I won't type less than in a designed-for-Intellisense API. And, No, it's not easy to grok. And for goodness sakes, please consider the affects of this coding style on the wider community of developers.

I'm sure you've studied how C# for each loops are implemented? With a brute force duck typing sort of approach? Back in 2007 [1], Krzysztof Cwalina was expressing a desire for a notation to support the concept. How about instead of dumping this onto the general C#/VB.NET web development community as a legitimate technique ("but Microsoft releases supported APIs that do it this way!"), you work with the Framework Guidelines team to get a verifiable notation down.

I know it'd be C# 4.0, I can wait.

[1] blogs.msdn.com/.../DuckNotation.aspx

What do you have to say?

(will show your gravatar)
Please add 2 and 4 and type the answer here: