January 2009

Volume 24 Number 01

Usability in Practice - When Things Go Wrong

By Charles Kreitzberg | January 2009

Code download available

Contents

The Usability Challenge
Error Messages Matter
Key Questions to Ask at the Design Stage
Formatting Error Messages
Programming Techniques
Consistent Handling
A Well-Constructed Error Message
Frameworks
Customizing Exceptions (with Enterprise Library)

kreitzberg.gif

Charles Kreitzberg

THE USABILITY CHALLENGE

From a usability perspective, error messages are often nightmares. Something has gone wrong with the program and it's up to the user to decide what to do next. Ideally, the program would produce an error message to communicate to the user what went wrong and how to correct the problem. Unfortunately, many error messages fall far short of this goal.

Consider the message in Figure 1, which appeared on my PC shortly after the boot sequence. Imagine the impact this message has on a non-technical user who has no idea what the problem is. The message implies that security has been breached and the situation is desperate. In fact, the situation was not quite so dire; I later identified the error message as coming from my video editor software and everything continued to run just fine. But it's amazing how many design flaws are contained in this message:

  1. There is no indication which program produced the error message.
  2. The message fails to explain why the program is being terminated.
  3. The message is very general. It says "the security information" without any reference to what the information is.
  4. The message does not indicate how severe the problem is and whether the user's computer is at risk.
  5. The user has no idea how to correct the problem or where to get more help. All he can do is press OK (which it certainly is not).

fig01.gif

Figure 1 Wow, Termination Sounds Painful

fig02.gif

Figure 2 Hmm, What If I’m Not Home When Windows Contacts Me?

No one is immune from confusing error messages. Even Microsoft, which has produced standards to suggest how to create good messages, ends up with an occasional clunker like the one in Figure 2, which I receive from time to time.

The Windows Vista User Experience Guideprovides guidelines for error messages that state that a good error message is built around three components:

  • States that a problem occurred.
  • Explains the cause.
  • Provides a solution so that users can fix the problem.

The guidelines also suggest that a good error message is presented in a way that is:

  • Relevant—the message presents a problem that users care about.
  • Actionable—users should either perform an action or change their behavior as a result of the message.
  • User-centered—the message describes the problem in terms of target user actions or goals, not in terms of what the code is unhappy with.
  • Brief—the message is as short as possible, but no shorter.
  • Clear—the message uses plain language so that the target users can easily understand the problem and solution.
  • Specific—the message describes the problem using specific language, giving specific names, locations, and values of the objects involved.
  • Courteous—users shouldn't be blamed or made to feel stupid.
  • Rare—displayed infrequently. Frequently displayed error messages are a sign of bad design.

The error message in Figure 2does not meet most of these criteria.

Error Messages Matter

Because end-user efficiency is so important, quality error messages are not simply a nicety. Errors bring processing to a halt, degrade the user experience, and carry a price tag. The cost of supporting users can become significant. Good error messages that enable the user to identify and correct problems can save a great deal of time, and money and minimize the impact on the user experience. But it's not always easy to produce good error messages.

By definition, error messages are produced when the program encounters an unexpected situation. The source of the problem may be far removed from the point at which the error is detected, and it may be impossible to figure out the original cause. While you cannot always control the environment, you can improve the situation by taking the time to design a useful message and translate the technical problem into user-centric and actionable terms.

Key Questions to Ask at the Design Stage

Here are some key user-centric questions you may want to consider as you think about how you will manage exceptions:

Who Is Your Audience?Are they developers, trained users, casual users, or the public?

What Types of Exceptions Will You Report?How do you want to present them?

What Support Will the User Have?Will there be Help Desk and Knowledge Base support, for example?

How Will You Get the User Back on Track?Simply notifying the user that a problem has occurred without offering an actionable way to resolve it will invariably lead to a negative user experience.

Do You Need Logging and Notification of Exception Occurrences?If so, you may need to consider how users or IT personnel will view, search, sort, filter, and maintain the logs and what the format and content of notifications will be. These should all be highly usable.

Should You Personalize the View to Different Audiences?Would, for example, a developer view and a public view be helpful? Does the use of external policy files to control the display open any security holes?

Can You Increase the Usability of Your Messages?And can you also preserve the link to the basic technical information by wrapping the exception in a new exception you create? Does passing technical information across an application boundary potentially carry any security risks?

These questions should be considered early—not at the end of the project.

Formatting Error Messages

The goals of error messages are to be as understandable, accurate, and actionable as possible. Strive to deliver:

  • As complete a picture as possible.
  • As much context as possible in terms that are meaningful to the user.
  • The suggested user actions in clear terms. Help the user be as comfortable as possible with the decision by making certain that the user understands the consequences of each choice.

Here are some suggestions to help you create good error messages:

Identify the CauseBe sure to indicate the site or application from which the error message originated. It may also be useful to indicate more in-depth information that can help locate the area of code where the error occurred, but be careful in exposing any internal details to users as such information could be used in malicious attacks. Separate additional diagnostic and lookup information from the main message so that it does not appear too technical for the end user. Consider showing details on demand (in a collapsible region, for instance) so the non-technical user does not need to deal with a technical presentation.

Use Plain LanguageExplain what happened using the least-technical language that you can. This can be difficult because you may not know what the user was attempting to do when the error occurred. However, the more you can relate the problem to what the user was trying to accomplish and the data involved, the better the user will be able to understand and potentially act upon it to resolve the situation. Again, be sensitive here not to expose information that could be used in an attack or to breach privacy. Explain the severity of the error and, if possible, explain the consequences of the problem.

Provide Details and GuidanceFor exceptions that are high level, such as "HTML Error -500 -Internal server error," attempt to provide more detail and specific guidance when you can. Explain the actions that the user can take to resolve the error and make certain that the choices and consequences are clear. If possible, provide a link to more information that can help the user make the appropriate decision. If the only option is to suggest that the user contact support, make certain that the contact information is accurate and easily updatable.

Orient the UserIf you use a custom error page in ASP.NET, make it visually integrated with your site and provide navigation to related pages (or at least the home page). Also, if you point users to a Web site with additional information do not dump them unceremoniously on the home page and leave it to them to locate the information; take them directly to the appropriate page.

little.gif

Ambrose Little

PROGRAMMING TECHNIQUES

I agree that error messages can be difficult, and all of the suggestions that Charles makes are very useful. So how do you actually implement them in your code?

For programming purposes, it is useful to divide error messages into two categories. Programmatic errors are exceptions that occur in code due to unforeseen circumstances; when a programmatic error occurs, there may be no option except to terminate the program gracefully. User input or validation exceptions are situations where the user may be able to correct the problem; for this type of error, a well-constructed error message may enable recovery. In this column, I am focusing on the first—programmatic errors—and I'll reserve user input and validation errors for another day.

Consistent Handling

Even in the best-written program, errors will occur. The best way to deal with them is to create classes for error messages that can be used throughout the program. By channeling all error message processing through a single point, you can do the following:

  1. Ensure that all messages are presented in a complete and consistent format.
  2. Use custom exception types and assign unique error codes for each message so that it becomes easy to produce a dictionary of error messages, expand it with additional information, and localize it if appropriate.
  3. If applicable, link error messages to a Web site that provides user support or at least provides some way for the user to contact application support with the error message.
  4. Create logs that can be automatically, or with user permission, shipped to a server and used to analyze problems and improve the software.

A Well-Constructed Error Message

The dialog boxes in Figures 3and 4show elements of a well-structured error message. If the error message is simple, you can use a box like the one in Figure 3, adapted from the Windows Vista Guidelines. This error message will be most useful for a user-correctable error. A programmatic error may require a more comprehensive message like the one shown in Figure 4.

Figure 3 Format of a Simple Error Message

Figure 4 Message for a More Complex Error

A good source of information on handling exceptions is the MSDN Library article " .NET Framework Developer's Guide: Design Guidelines for Exceptions." While you are at the MSDN Library, take a look at the article " Exception Handling Application Block." It has the capability to wrap an exception, nesting it inside of an exception that you create with more information. This can be used to add additional information that captures the context in which the exception occurred. You can use this information to create more user-centric error messages.

If security is a concern and you want to avoid passing on technical information, you can replace the exception with a more user-centric one, rather than wrapping it. Finally, you can use the Exception Handling Block to log the error and notify appropriate stakeholders by e-mail, Windows Management Instrumentation (WMI), or some other custom mechanism.

If you are looking for a lightweight error logging and reporting facility for ASP.NET, take a look at Error Logging Modules and Handlers(ELMAH). It offers logging, several log storage options, the ability to view the logs on a Web page, e-mail, and RSS notifications. And you can create an ELMAH exception handler for EntLib, such as the one freely available on dotNetTemplar. That way you get the infrastructure that EntLib provides and the reporting services that ELMAH provides together.

If you are building applications for a Windows XP or Windows Vista environment, you may want to consider the Windows Error Reporting Service (Dr. Watson). When a user submits crash data, the Windows Error Reporting Service checks to see if there is a user message associated with it and presents the message at the time of the crash.

While these frameworks are excellent solutions for managing exception handling, using them does not guarantee usability. You still need to view the situation from the user's perspective by trying to get an understanding of the user's technical understanding and crafting the most clear, accurate, and actionable presentation.

Now let's take a look at an example and some sample code that demonstrates the concepts discussed thus far.

Customizing Exceptions (with Enterprise Library)

The following outlines an example of how to leverage the Enterprise Library Exception Handling Block to customize exceptions for a Windows Forms application; a similar technique could be employed for other Microsoft .NET Framework UI technologies.

First, centralize exception handling through some kind of ExceptionHandling class, as seen in Figure 5. This is a static class with methods that map directly to defined exception policies in your Enterprise Library configuration.

Figure 5 The ExceptionHandling Class

public static class ExceptionHandling { public static Exception UiUnknown(Exception exception) { try { if (Microsoft.Practices.EnterpriseLibrary.
ExceptionHandling.ExceptionPolicy .HandleException(exception, "UiUnknown")) return exception; } catch (Exception ex) { return ex; } return null; } }

Configure that policy in the Enterprise Library Configuration tool, as shown in Figure 6. Specify two type handlers, much like you might in a try/catch statement. The more specific handler is chosen. For both, you can first log the thrown exception using the built-in XML exception formatter with an XML logging trace listener. For the general exception, you can wrap the source exception with a new exception that has a user-centric message. You could alternatively replace it, but just in case the UI wants to have the details of the actual exception, it may be better to wrap it.

Figure 6 The Enterprise Library Configuration Tool

One important little trick you need to do when wrapping or replacing is to set the PostHandlingAction on the exception types to be ThrowNewException—this way it will throw the new, wrapped exception.

If you want the calling code to do more with the exception, the other option is to choose NotifyRethrow, which causes the EntLib ExceptionPolicy.HandleException to return true. As you'll see, for the DatabaseConnectivityException, you can handle the user-centric stuff in your custom exception type, so all the previous policy does for that is log it and notify that it should be rethrown.

From the call to ExceptionHandling.UiUnknown you get back the new, wrapped exception, the original exception, or none if the policy chooses not to throw a new one or notify to rethrow—that's a key benefit of the exception handling block—you can configure your policies outside of the application code itself (a good way to keep that cross-cutting concern out of application code). Then pass that into the ErrorMessage.Show method, which is defined in Figure 7on the ErrorMessage form.

Figure 7 The ErrorMessage Form

public static void Show(Exception relatedException) { ErrorMessage em = new ErrorMessage(); if (relatedException == null) { em.problemDetailsContainer.Visible = false; }
else { em.problemDescription.Text = relatedException.Message; IUIExceptionDetail detail = relatedException as IUIExceptionDetail; if (detail == null) { em.errorCode.Text =
"500"; em.problemDetails.Visible = false; } else { em.errorCode.Text = detail.ErrorCode.ToString(); em.problemDetails.Text = detail.DetailMessage; } em.problemType.Text = em.GetMeaningfulExceptionType(relatedException).Name; em._SearchText = em.errorCode.Text + " " + em.problemType.Text; } em.ShowDialog(); }

The ErrorMessage form is a friendly screen that has company branding, contact information, and offers the ability to report the problem to the company as well as search online for a solution.

If no exception is given, it hides the details container—no need to mislead users into thinking there are details if there aren't any. (If that's the case, you may not want to even show a message.) If you do have an exception, set the problem description to the exception message. Then check to see if the exception type implements the custom IUIExceptionDetail interface.

This interface allows you to intelligently provide more meaningful exception information to users when you can. The interface looks like this:

public interface IUIExceptionDetail { int ErrorCode { get; } string DetailMessage { get; } }

Using this, you can create custom, meaningful, user-centric exceptions that can help users potentially solve the problems on their own or at a minimum provide a good error code that serves as an easy reference key for searching and support. The sample implementation of this interface is the DataConnectivityException type shown in Figure 8.

Figure 8 DataConnectivityException

public class DatabaseConnectivityException : Exception, IUIExceptionDetail { public DatabaseConnectivityException(Exception innerException) :
base(Resources.Exceptions.DatabaseConnectivityException_Message, innerException) { int.TryParse( Resources.Exceptions.DatabaseConnectivityException_Code, out
_ErrorCode); _DetailMessage = Resources.Exceptions .DatabaseConnectivityException_DetailMessage; } #region IUIExceptionDetail Members int _ErrorCode; public int ErrorCode { get { return _ErrorCode; } } string _DetailMessage; public string DetailMessage { get { return _DetailMessage; } } #endregion }

The most interesting thing here is the use of a RESX file to store the message, error code, and details. This allows for localization, but it also serves as a handy XML source for error message documentation—you could easily apply XSLT to the RESX format to produce some HTML or other rich text markup.

You may find this approach preferable to the more basic wrap/replace facilities that EntLib provides, and that's fine—you don't have to use EntLib for providing user-centric messages, but it is still good to externalize your exception handling into policies for logging and at least giving you the ability to customize exception handling outside of the application itself.

Looking back to the ErrorMessage.Show method, you can see the check for the IUIExceptionDetail interface. If it is found, it uses the extra supplied information to show to the user. Otherwise, it hides the details box and shows a generic error code of 500 (similar to HTTP 500).

The result for a generic/unknown error is shown in Figure 9. For a more specific error, like the DataConnectivityException, you get a message like Figure 10.

fig09.gif

Figure 9 Generic Error Message

fig10.gif

Figure 10 More Specific Error Message

There are a few things to note here. First, this is following the Windows Vista guidelines for error messages fairly closely. The window is a modal dialog. The dialog title tells users clearly what is generating the error so that they don't, for instance, confuse it with system errors; this could also be a specific feature of the application. There is a prominent indicator that this is an error with the large icon, which is overlaid on the application logo, serving the dual-purpose of reinforcing the source of the problem while clearly communicating it is an error. The title text also prominently tells the user that there is a problem with the app.

Right below the title shows the exception message. This should be concise but courteous and written in terms the user would understand. If possible, it should also offer solutions. In the case of the general error, you might not know, so the best approach in that case is to instruct the user to report the error or search online. In the case of the connectivity, you can supply some extra suggestions if you know likely solutions. The ideal here is to try to empower the user to resolve the problem on his own, which is why very specific messages are preferable to general messages.

fig11.gif

Figure 11 Tell Us about the Problem Is the Featured Button Ambrose Little

The "Search for Solutions Online" link button will launch a search using " MSDN Magazine" plus the error and exception type. This is a nice feature, as users might not know what to put into the search box. If you have a user support section on your site, you could also limit the search to your site. And of course, if you have a specific lookup service (based on code), you should prefer that over a general keyword search.

The "Tell Us About the Problem" button should ship the XML log to a Web service. You might want give the user a chance to provide any contextual notes and/or contact info, and the service should return some tracking info that the user could use to reference if they choose to call in.

An alternative display you could use is shown in Figure 11. The value here is that the Tell Us About the Problem is the most prominent command on the screen. One thing to think about in your UI design is guiding users based on visual hierarchy; in other words, if something is more important or, in this case, is the command that you want to encourage users to use, ensure that it is the most prominent, obvious, and easy to use. You have to decide based on your context which actions are more important and, if there is a hierarchy, communicate that visually.

Once you have this in place, you can add an application-level handler in the Program.cs Main method like so:

Application.ThreadException += Application_ThreadException;

That method looks like this:

static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { ErrorMessage.Show(ExceptionHandling.UiUnknown(e.Exception));
}

This catches all unexpected UI exceptions and routes them through the "UiUnknown" policy in your centralized exception handler class, which in this sample delegates handling the Enterprise Library, though it could be whatever exception handling framework you have. That method will potentially return an exception to show via the more user-friendly ErrorMessage form that you designed. And, of course, you can use a similar approach for more specific exception policies throughout your application.

As you see, creating good error messages is not easy, but taking the time to carefully structure them can save a great deal of user effort and reduce frustration.

Dr. Charles Kreitzbergis CEO of Cognetics Corporation, which offers usability consulting and user experience design services. His passion is creating intuitive interfaces that engage and delight users while supporting the product's business goals. Charles lives in central New Jersey where he moonlights as a performing musician.

Ambrose Littlelives with his wife and four children in central New Jersey. He's been designing and developing software for more than 10 years and is honored to be an INETA speaker and Microsoft MVP. Lately, he's shifted from technical design to designing for people and is now a user experience designer for Infragistics.