Archive for October, 2007

The Usability of Programs, Part 2 of 2

Monday, October 22nd, 2007

Here are some other principles of UI design that apply to program design:

Reduce Cognitive Load. This guideline in UI design is about minimizing the amount of things a user has to know in order to be able to use a tool effectively. Likewise, a programmer shouldn’t have to subclass five different classes just to effect one small change to an application. For instance, I often find that in order to add a new behavior interactive application I may have to learn the logging framework, the undo/redo mechanism, the localization framework, and the validation framework–on top of whatever complicated hierarchies exist in the model and views. Things usually reach this state when developers anticipating future needs and striving for reusability and generality in their frameworks end up sacrificing simplicity and, ultimately, usability.

DaVinci VmanBe consistent. The brake pedal on one model of car shouldn’t serve as the accelerator on another. That would be dangerous. It can also be dangerous to mix thread safe classes with classes that aren’t thread safe, or to have methods accept null String values in some places and throw exceptions in others. If you adopt an idiom in your public interfaces, like converting exceptions thrown by delegate APIs to local exceptions, or using immutable objects as enumerated types instead of ints, then try to stick with it. Consistency fosters a perception of predictability and stability.

…But not too consistent. Jakob Nielsen, a well known usability expert, suggested that you can take certain principles too far, and the principle of consistency is one of those. He felt that Mac interfaces went to excess in trying to maintain consistency, to the point of defying common sense. A famous example: since the operation of removing things from a desktop consisted of dragging them to the trash can, the operation for ejecting a floppy disk was to drag it from the desktop into the trash can, an operation guaranteed to unnerve a user the first time they try it. In software, programmers often step into the realm of over-engineering in an effort to enforce consistency. An action in a user interface may implement an Undoable interface, but that doesn’t mean all actions should be undoable. One might take pains to separate UI code from Domain objects, but if it means introducing intermediary classes and interfaces just to keep domain code from importing javax.swing packages then it may be better to diverge from consistency.

Avoid modality. In many applications users are confronted with modal dialogs which force them to take action before they can proceed in other parts of the application. While a modal dialog is showing, all other parts of the interface are disabled. On the surface this may seem helpful to the user but oftentimes it’s just a convenience for the programmer who doesn’t have the inclination to design the data model so that multiple activities can be conducted concurrently (i.e., changing the properties of an Employee in a dialog while simultaneously deleting that employee in the parent window).

The same thing applies in program design. Sometimes when one object collaborates with another it makes assumptions about the state of that object. Its operation is only guaranteed to be correct under those assumptions. For instance in some data access object there may be operations which result in updating a database object. But these operations might assume that a transaction has been started and fail in the worst possible way if that’s not the case. Users of this class must be aware these methods can only be called in a transaction and keep track of the state of the transaction. An alternative design might encapsulate individual operations in a full transaction, or offer more support keeping track of the transaction state.

Avoid this kind of modality in your classes. You can’t drop all preconditions but the fewer opportunities for failure, the more robust the application.

HandleUtilize Affordances. Affordances are properties of an object that suggest some action can be taken. A door knob “affords” opening a door. A rectangle on a screen may not afford action, but if it is raised and rounded, resembling a button, it affords being pushed. For programmers, affordances might be properties of an interface which make it’s usage more clear. In a class, abstract methods afford overriding. If a class requires several instance variables be initialized when the instance is created, then the programmer should afford that action by adding parameters to the constructor to populate those variables, rather than rely on the caller to invoke setters. Good abstractions and well factored class hierarchies can afford subclass extensions, where flat class structures with duplicate code and concrete classes may not.

Adopt Standards. What more is there to say? Standards in User Interface design result in uniformity across user interfaces, giving the user a sense of familiarity when using an application for the first time. In programming, coding standards provide exactly the same benefit.

Provide Feedback. Feedback in user interfaces usually means giving some indication that a user action has been detected and interpreted. If the action was accepted then give an indication to the user of success. If the action results in an error, make that error known to the user directly. This could probably be interpreted several ways in the context of programming, but the best one would probably be the principal of “Fail Fast.” This means detecting an erroneous use of a class or method early and communicating the actual offense, preferably in a runtime exception, as opposed to letting it go and waiting until the effects of the error cause secondary exceptions which may not reveal the actual API violation. An example of this would be not checking for null values in method parameters but passing the null values on to subsequent calls until a NullPointerException appears in a lower level method. I don’t think you want to check every pre-condition to an exception since it makes methods very noisy, but a lot of times there are conditions which are more likely to be violated. Play the odds and enforce those when you come across them.

You can really go on and on with this idea. The principles of usability apply to any system that interacts with a user, and that includes a programming model. The goal of a great programmer should be the same as the great product designers: empower and delight your users. Programmers should never forget that ultimately their users are not CPUs but other programmers.

The Usability of Code, Part 1 of 2

Friday, October 12th, 2007

Not long ago Ken Arnold wrote an article for Queue magazine called Programmers are People Too. In it he talks about applying the principles of User Interface Design to the design of APIs. It turns out the practices of Human Factors and Usability Engineering have a lot to offer the implementors of public APIs and frameworks. Think of an API as the “user interface” of a larger programming model. Users are application programmers and the usability of the API is judged not just by it’s richness, but on the ease of use and learnability of the API. It’s not sufficient to address usability with thorough documentation any more than it’s sufficient to compensate for a bewilderingly difficult user interface to a mobile phone with a five pound user manual.

Remote Control InterfaceThe idea of applying Human Factors to API design is not far off the idea of designing programs as works of literature. In both cases your main focus is your audience, not the computer. The common principle in both is readability, but the idea of API usability takes it a little further because your audience isn’t just trying to understand what it’s looking at but actually use and extend the work.

I believe these principles of usability apply not just to API design, but to programs in general. Ken Arnold gives a couple of examples of UI principles applied to API design, such as the use of Progressive Disclosure. That’s the principle that says you shouldn’t overwhelm a user with more detail than they need for a common task, but rather disclose the additional detail when the need arises as they move further along in some interaction. Give the user what they need to know when they need it and only when they need it. That way they won’t be distracted or confused by superfluous information.

For an API, this might mean segregating the methods of a complicated class into the methods most commonly used by client applications from the less frequently used methods needed in special situations or by advanced users. For an application designer it might mean using interfaces and abstract classes to hide the distracting implementation details of a class.

Think about other common principles for user interface design, such those described in the Apple Human Interface Guidelines. How can these be applied to program design? I’ll list some more examples in the next installment of this column.

Programmers as Channel Surfers

Tuesday, October 2nd, 2007

If a program is to be a work of literature, then where does that leave object-oriented programs? In the era of functional decomposition, programs were built around algorithms and data structures. They executed in a single thread as a sequence of steps and subroutine calls, not unlike a story. Our object-oriented programs, on the other hand, don’t have such a fixed path of execution. Whether server or client applications, they start up multiple threads and make heavy use of message passing and asynchronicity. The essence of a program is not in a sequence of steps and subroutine calls but in the subtle relationships between classes spread across many frameworks and subsystems, relationships such as inheritance, aggregation and collaboration. So how does one read a Java application like a story? The whole idea of object oriented programs seems at odds with literate programming.

It may help to view your program less as a novel read in a single sitting and more like a TV show that is tuned in once a week by busy people with short attention spans. The purpose is still to engage and inform, but you need to keep in mind your audience could be jumping in to the story just about anywhere. Like a viewer flipping through channels, happening upon an interesting show, there’s a good chance that the first time a colleague comes across your code is clicking through the frames in a stack trace. They will be debugging an application trying to understand some unexpected sequence of events and will stumble upon one of your classes. Or they might drop in to use some small part of your API. Maybe they jump into your code to extend some capabilities, add a new feature, or make small changes as part of a global refactoring. They just want to know enough to get the task done and keep going. They might watch an episode but then move on.

There are a lot of practical implications to this approach but for this article I’ll just focus on one: give the user the information they need in the context of just “dropping in.” Think about the questions they might have looking at your code in a debugger: What is this class for? What elements are in the collection? Where did this member variable get set? Don’t make them work to find out something you could have made apparent with very little effort to begin with. Your goal is to give them enough information about each element so they won’t have to get sidetracked tracking down other references to the element just to see how it’s used . For example:

  • Don’t make it a precondition for them to read a big design document on your intranet just to be able to understand what your GizmoClass is. Put the information as close to the artifact as possible. In the code, or at least in the same directory as the source.
  • Document all your classes and interfaces. This may seem like a no-brainer, but it’s not about spending more time writing comments. Instead, keep it as simple as possible. Avoid verbosity and forms. Just a sentence or two to explain what the class is (especially helpful if you couldn’t come up with a really good name). Maybe say what it’s used for. That’s usually about as much as someone needs to know. Refrain from a huge exposition on the overall design or the justification for the class. Save discussion of the class members for the member comments themselves. And if you can’t think of something meaningful to say, don’t say anything at all.
  • Don’t duplicate information in an Interface and a Class. If you have to pick one, document the interface. Focus on the interface for describing what something is or represents, and in the implementation, anything noteworthy about the implementation.
  • When declaring variables of collection types, if you say nothing else in the comment you should at least state the actual type of the elements of the collection.
  • Method javadocs are critical for the casual audience, but not because they will show up in the HTML documentation. Most IDEs will provide javadoc comments in a tooltip or property sheet, saving the user the step of clicking through to the declaration, or opening up the javadocs in a new window.
  • Make variable names as descriptive as possible. Not just your member or local variables. People tuning in to your code the first time will have a much easier time decrypting your for loops if instead of i or index you use names like row, month, or customerNumber.
  • Consider “Hungarian Notation” for variable names. Many developers find this convention ugly and onerous, but you can’t deny the value of immediately recognizing a variables scope without having to jump to its declaration. There’s a wide spectrum of adherence to this principle; from the old C++ practice of limiting the convention to member variables with a ‘_’ prefix on one hand, to an array of prefixes identifying the scope of every variable.

The point of this is not to enumerate a list of documentation standards for your programs, but to get you to think like an author and empathize with your audience. Use your own experience learning and debugging other people’s code to determine what your audience needs from your documentation. Give them whatever they need to keep them from changing the channel before your program finishes.