Monthly Archives: April 2023

Programmers: Make Things Easy and Fast for Your Users

I’ve been writing software for over 50 years and have evolved a set of rules for designing user interfaces that are easy to use and which allow users to get their work done as quickly as possible. These rules apply equally to desktop applications, apps, and web pages.

I don’t know what they’re teaching in user-interface classes these days, but software today is, as a rule, dreadful. It is extremely painful for me to use since I see glaring design failures at almost every step. Clearly, the rules people are learning are inadequate.

Basically, all my rules are intended to reduce the number of steps needed to accomplish something. That basic principle is at the root of good user-interface design. I think that the focus these days is on appearance, but appearance should be secondary to function, which is not to imply that you have to sacrifice aesthetics to achieve ideal functionality.

Here are some basic rules:

Eliminate Unnecessary Steps

Every step in a multi-step process should be examined to see if it is absolutely necessary.

Make use of defaults and persistent settings to eliminate steps related to options. Often an operation that needs to be repeated uses data from the previous operation. Make it easy, or at best unnecessary, to enter duplicate information. For example, use something like Ctrl-D to copy the previously-entered value from a field, or default to the previous value.

Very often a multi-step process can be reduced to one or two steps by careful design. This can greatly increase the speed at which users can get their work done, and make them much happier.

Be Judicious in the Use of Security

Most programs and websites are accessed from insecure computers, and don’t involve information that has huge consequences if access to it is compromised. Also, many people use password managers which automate logins for anyone using the computer.

So probably most users just find it annoying (and completely unnecessary) to have to log in each time. Avoid the need to log in, or have an option to stay logged in. Consider Amazon, which has moderate consequences if an unauthorized user gains access — they keep you logged in pretty much forever. They want it to be easy to buy things, and logging in makes it less easy.

Eliminate Most “Are-You-Sure” Dialogs

Few things are more annoying than being asked “are you sure?” after you’ve already indicated that you want to do something. Some software does this for almost everything you do, and in most cases it is completely unnecessary.

The key question to ask is: what are the consequences of making a mistake here? If a mistake will cause significant work to be lost, yes you should ask. But if the cost of recovering from a mistake is low, you don’t need to ask. Compare that cost with the cost of having to answer “yes” in the vast majority of cases.

If you still think you should ask, see if other design changes could eliminate or reduce the need for the dialog. For example, a “restore” or “undo” option would do the trick. Often the best solution is a multi-step “undo” function that will undo everything except exiting without saving, and even that can be overcome by automatically saving the changed data in a temporary file before exiting without saving.

Make the Most Common Next Step Easiest to Do

Most programs are used in predictable ways. Frequently there are sequences of steps that repeat often. Make the path through that sequence as straightforward as possible.

Make the thing most likely to be done next the easiest to do. Minimize the movement necessary to accomplish it, for example by putting targets closer together. Don’t have targets that are inactive displayed.

The classic example of failing to follow this rule is websites that have a search box, like the Amazon site. The first thing most users want to do is type something in the search box. But first the user has to click in the search box to get the cursor there. If the site came up with the cursor already there, the user could just start typing. There’s no reason I know of why that isn’t done; it doesn’t mean the user has to do more to click on something else if he doesn’t want to search. Worse, many people (like me) assume that they can just start typing, and they type a bunch of stuff before realizing that nothing is being entered.

So, if a screen comes up with a box to type into, and putting the focus on that box doesn’t add extra steps to other things, by all means put the focus there. The only exception I can think of is when the most likely next step is to scroll the page, in which case the focus should stay on the page.

Minimize the Use of Icons

The most recognizable icons are words. Use them. How many times do you have to move your cursor over a series of icons to see the text that pops up? Eliminate that annoyance by just using words instead. People don’t remember any but the most recognizable and frequently-used icons, so they just slow down their work and increase errors.

Yes words often use more space on the screen. Good design will compensate, and will reduce clutter. It also helps to only display functions that are relevant to the current state.

Choose Words Very Carefully

It is extraordinarily important that you use the clearest wording possible. This is especially critical when naming functions, where names need to be both clear and short.

In the 70s and 80s I was the lead designer of a CAD system that ended up being the main product of a 100-person company and which generated tens of millions in sales. The tag line for it in ads was “It’s Easy!”, and it was. When we named things for that product, there was often a meeting, sometimes a long one, just to come up with be best word to describe a single function.

Messages, especially error messages, need also to be very carefully worded. When you are composing a message, think carefully about it and about how others might interpret it.

Button wording is also important. Stick with conventional words like “OK” and “Continue” when appropriate. Be very careful to avoid words with ambiguous meanings. When a button does something, label the button with what it does.

Program Errors

It seems that the convention for finding program errors says is to write programs full of “debug” statements then to turn off the debug checking for release builds. The result is that internal errors that occur in programs will cause crashes or malfunctions when something goes wrong. I’m a strong believer in having release builds contain extensive internal checking and that even release builds report such errors when they occur.

I realize that it is politically bad for programs to report internal errors. My response is that doing so makes it possible (and easy) to identify and fix bugs. Overall, users will see less bad behavior from programs that report errors than from programs that don’t, since bugs will get identified and fixed early.

There are many ways to record and report internal errors. The simplest of course is to just display a message and exit. With a little more work, error details can be recorded in the Windows application log. What is essential is to see that errors that occur get reported to the developers. Ideal is an email or other message to the developer reporting the error.

It is also very important to report the error in an identifiable way. Reports should identify the error explicitly, and include a call stack. The way I do it is to have an error reporting function that does this, and which terminates the program. Terminating the program is essential to motivate users to report errors and developers to fix them. A call such as “gterm(“func3″,123)” produces a message like:

Terminal error FUNC3 123
app->func1->func2->func3

Which is generally all a developer needs to identify where and why the error occurred as well as what the user had done to cause it to occur.

If you write highly structured code this may not be sufficient, in which case you might want to add information about classes and such to the call stack information. Also, if you make extensive use of third-party libraries, you may need to find ways to deal with their opaque nature, perhaps by writing wrappers for the library functions.

You can use a similar technique to report unhandled exceptions.