Designing as if Programmers are People
Ken Arnold, the original lead architect of JavaSpaces, talks with Bill Venners
about the myth of "the perfect design," simplicity, taste, and the
importance of designing with the user in mind. This article features some highlights from a sequence of conversations between Ken and Bill that originally appeared on Artima.com.
Ken Arnold has done a lot of design in his day. While at Sun Microsystems, Ken was one of the original architects of Jini technology and was the original lead architect of JavaSpaces. Prior to joining Sun, Ken participated in the original Hewlett-Packard architectural team that designed CORBA. In this interview, Arnold explains why there's no such thing as a perfect design, suggests questions you should ask when you do a design, and proposes the radical notion that programmers are people. Arnold also discusses the role of taste and arrogance in design and the value of other people's problems.
The Myth of the Perfect Design
Bill Venners: I once heard you say there is no such thing as a perfect design. Could you clarify what you meant by that?
Ken Arnold: There is no such thing as a perfect design for a couple of reasons. One is that all designs take place in context. You are trying to solve a particular problem. Although you might state in a general way, "I am trying to put together a linked list container," the fact is that linked list will be used in a particular context. And whatever design trade-offs you choose to make the linked list reasonable for that context will be bad for other contexts. For example, in your context, are there more insertions than deletions? Is there more reading than modification? Depending on the answers to those questions, you might make certain optimizations rather than others.
Another issue is who will be programming with your linked list. Are you giving it to a group of rocket scientists who you can trust will do things right 98 percent of the time? Of course, you'd be wrong, but you could hope they'd be right most of the time. Are you handing it to relatively inexperienced people? Or are you handing it to people who, despite being very experienced, aren't focused on how to use the linked list? Instead, they are focused on some other problem, so their understanding of the linked list will be casual. If so, you have to design to prevent errors. Designing to prevent errors can make your job more difficult. You may have to trade off error prevention for functionality, or at least ease of use. So part of the reason no perfect design exists is that no matter what you do, you end up making one design trade-off over another. You make choices that are right for your context, but bad for some other set of circumstances.
Another reason, which is in a different category, is that if you try to create a perfect design, you will expend a huge amount of effort. You will go through all sorts of behavior patterns. You and the architects meet and think about this perfect design mightily. And you argue for hours over, say, whether
next should be synchronized. It is a vast sink of time. The difference in effort between delivering something perfect, as opposed to something good, is significant. It just isn't worth the trouble in 98 percent of the cases.
And then there's the problem of predicting the future. Whatever you design for, whatever set of trade-offs you choose, no matter how hard you work at it, your linked list will be used in ways you didn't expect. Perhaps you thought there would be more modifications than reads, but it turns out you were wrong. If you over commit to any of those directions, you can hurt your design.
The best that people can reasonably hope for is to put forth an appropriate amount of effort and get a good design that is sufficient. Your design should do as little as it can to get the job done, so you can add the right thing when you discover what that right thing is.
Keeping Interfaces Small and Simple
Bill Venners: One thing that impresses me about the
JavaSpace interface is that it has only seven methods. It is very simple, and yet I can do a lot with those seven methods. How did you achieve an interface design that was both simple and powerful?
Ken Arnold: With JavaSpaces, in particular, we had the great of advantage of leveraging off of the Linda work. We started with Linda's simple design as a launching point. It was easy to see that we didn't need to do X, Y, and Z, because they could be done on top of the API. Maybe someone would want to write an API that does those things on top of JavaSpaces, but those things didn't need to be in the
There is an important distinction between "What does this tool do well?" and "What can you write that uses this tool to do something else well?" When I design, I try to make each actor within the design -- each class or interface, for example -- do one thing right. I also try to never couple what isn't intrinsically coupled. This is a general design principle.
For example, look at what happens in, say,
ServiceRegistrar, Jini's lookup-service API. I don't remember the number of methods it has, but it is comparable to the
JavaSpace interface, maybe ten methods. And the Jini group didn't have other work, except in principle, to reference. But
ServiceRegistrar comes from that same design principle. Remote method invocation (RMI) is another example. It is relatively simple to write an RMI system compared to the traditional CORBA remote procedure call (RPC) systems. I think that comes from focusing on what the user needs to do, rather than what the user might want to do.
Many people approach designs by asking, "What might the user want to do?" If you start with that question, the set of answers is infinite. You end up with interfaces that have 80 or 90 methods, or 50 or 60 interacting classes. Look at Swing's
JComponent class, for example. It has well over 200 methods.
JButton, as a
JComponent subclass, inherits those methods. But someone dealing with a button cares about five of those 200-plus methods, most of the time. The other methods are interesting in odd cases, so they should be set aside somewhere. But they're all in there together and you don't know what to touch.
There is a phenomenon I call the surface area of design, which is what you must understand about a design to know which part you care about. Does the fact that there are more than 200 methods mean anything to the
setText() method? You must know something about those 200 methods to know the answer to that question. You have to know what to ignore. Many people say they don't need to look at all of those other methods. But you do need to know if they will interact with the methods you do care about. The problem is you need to know enough about those other methods to know you don't need to understand them.
Asking the Right Design Questions
Bill Venners: Simplicity is a design virtue, but can a design be too simple? If you eliminate some feature in the name of simplicity that is truly useful to most users, you've actually made their jobs more difficult. Since you didn't provide that feature, they have to build it themselves or do without it. How do you know how simple to make things?
Ken Arnold: Yes, at some point a design is so simple it doesn't work anymore, right? I think the fundamental way you get simple, yet sufficient, systems is to ask yourself some questions. First, what does the user need to accomplish? Let me back up and explain what I mean by user. I have this really weird radical notion. Every great idea starts off with an absolutely radical notion. I am immodest enough to think I have this great idea. And the radical notion that founds it is that programmers are people. Now if you accept this premise, then the next step is to say that designing tools for programmers, including languages, APIs, and compilers, is a human factors problem. And so we should ask the same kinds of questions that people ask about graphical user interfaces (GUIs). Is it easy to do what you need to do? Is it natural? Are simple things simple? Are complicated things complicated? Are dangerous things hard to do? Are common things easy to do? Are similar things done in a way that is naturally similar to the person?
You start asking all these questions. And if you do that right, like with a GUI, you come up with something easy to use. It may be rich and complicated, but it has an easy starting point and easier mechanisms to learn things.
However, the more common way to think of design is, "What can I, the designer, do?" instead of, "What does the user want to do?" For example, we could have done many things with JavaSpaces, but our capabilities were of no value. It is the user's desires that were of value.
The questions I fundamentally ask are: "What do users want to do? What do they have in hand when they want to do it? What kinds of information do they have to accomplish this task? And how do they think of this task?"
Thinking from the User In
Bill Venners: How do you figure out what the user wants?
Ken Arnold: It takes a lot more work to understand the other person than it takes to understand yourself. You might say, "I have these two things together so I will let the user -- the person using the API -- manipulate them." But that user didn't say, "Hey, I want to manipulate these two data structures!" Often, the user is saying, "I want to get this result." If users could get a result without manipulating the data structures, they'd be happy as clams. If you can make it more natural for them to get that result, the fact that you have to go through 10 times as much work to access those data structures is good; it means you are providing value. Many people are much more likely to think about what they have in hand and what they can do. They think from the implementation out, instead of thinking from the user in.
To me, thinking from the user in is the designer's job. If you just want to implement, you should work for somebody who knows how to design. If you want to design, you have to ask, "Who am I designing for? What do they want to accomplish? And what do they have at their disposal to accomplish it?"
Does the user already think of this parameter as an image or a data stream? If the user thinks of it as a data stream and you think of it as an image, then that is your problem, not theirs. They should hand you a data stream, and you should do whatever you need to do given a data stream.
Or maybe it is the other way around. If you look at Jini, many times you will hand something to the Jini APIs that can't be transmitted across the network as is. Some methods of the lookup service, for example, take real
Class objects. But you don't want to move
Class objects around the network in that form. Everyone who implements the lookup service, therefore, must translate the
Class objects into something that can be moved around the network. Well, that is just their problem.
That means the user doesn't have to call something that turns a
Class object into something transmittable. And not only is that easier on the user, but it allows more variation in the implementation, because you can have one kind of transmitter but I can have another. And the whole issue of transmittability is an implementation detail.
Design, fundamentally to me, is a human factors experience. And yes, these humans are programmers. And they are a little different than other people. They may be better at formal thinking. Maybe they remember more than the standard seven plus or minus two. Maybe they can remember nine plus or minus two, or eight plus or minus three, who knows. But they are still people.
Another thing to keep in mind is that people will do stupid things. To you, everything you write is important. To the person using that design, it is a means to an end, and they want to understand as little of it as possible. As a result, you want to make illegal or improper things impossible. Any mistakes you can't make impossible, you want to catch. In other words, first design such that improper things can't even be written or expressed. They are impossible to do. Errors you can't make impossible, you want to catch right away. So as soon as a user makes a mistake, he or she is told.
Don't give people a method that does something error-prone. Give them the method that allows them to do the subset that is not error-prone. Suppose you have a file object on which you can call
close. You can only close an open file. You shouldn't be able to call
close on the file object, because it may not be open. The
open method should return something on which you can invoke
Therefore, you can't invoke
close on something you have never opened, because the only thing you can invoke
close on you get from
open. Now you can't express the error. So you don't have to catch the error of attempting to close an unopened file, because you can't even write that.
Taste and Arrogance
Bill Venners: In an interview with chief Jini architect Jim Waldo, Frank Sommers asked Waldo how he assembled such a talented team of people in the Jini group, Waldo responded, "Much of [building a good team] is just finding people who have the right sense of aesthetic -- a lot has to do with taste, and a desire to keep things simple and elegant." What does taste have to do with design?
Ken Arnold: When you are faced with a design problem, it is important to ask the question, "What does the user want?" The answer to that question has a lot to do with taste.
It's funny. To some extent you can answer that question empirically. You can ask users what they want. But sometimes people say they want something when they really want something else.
You cannot apply your understanding of human beings in a purely rational way, because human beings aren't purely rational. In the end, you have to use some sense of judgment. You may say, "People think they want to do this, but frankly, they're wrong." Or, "People don't realize this is a good way to approach it, but if I present it to them they would get it." And those are intrinsically arrogant statements. You are essentially telling users that in this area, you know more than they know about what they should want. But that kind of arrogance is a form of aesthetic.
Say you have a guy who paints your house. He'll paint it any color you want, but he doesn't want to paint something that looks ugly. If you suggest one color, he may push you the other way. Eventually, since you're the boss, he'll do it if you insist. But his sense of aesthetics leads him to tell you, "You think you know what you're doing, but I actually have more experience in this area. I know better how to implement what you say you want."
That is the sense of aesthetics required to design things. Your sense of aesthetics could be right or wrong in a given case, but you must have confidence in it. You must trust your interpretation of how irrational human beings will actually do something and what they will actually need.
Bill Venners: How do you learn what is good and tasteful in design?
Ken Arnold: Some people have a natural sense. Other people have to learn. But to a large extent, what people have to learn is to value other people's problems.
Taste is a very personal thing. There is no textbook. When people ask me about books on object design, the books I hand them have nothing to do with object design per se. I commonly recommend The Design of Everyday Things, by Donald Norman. This book promotes focusing on usability in the design of doors, teapots, and
faucets -- everyday things. If you do that for objects, you'll have the idea.
Again the questions are, "What is the user coming at the problem with? What are they trying to accomplish?" Most people who have what I call bad taste are talking about themselves. When I express things in bad taste, from which of course I am not immune, I am usually talking about myself. I am not integrating my understanding with other people's understanding. If you can merge those together, then you can acquire good taste. So teaching people taste is mostly teaching them how to listen and how to put themselves in someone else's place.
Instead of thinking from the implementation out, people need to think from the user in. As a designer, you may have two data structures that the user could merge to get a result, but do you understand that users don't care about your data structures? They just want to get the result. That is a revelation for many designers. Someone may have very good aesthetics if the problem is how to merge two data structures to get a result. But you can show them that that is not the question. The first step on the road, which many people miss, is asking, "What is the other person trying to do?"
This interview includes excerpts from a six-part interview with Ken Arnold originally published on Artima.com:
"Perfection and Simplicity: A Conversation with Ken Arnold, Part I":
"Taste and Aesthetics: A Conversation with Ken Arnold, Part II":
"Designing Distributed Systems: A Conversation with Ken Arnold, Part III":
"Sway with JavaSpaces: A Conversation with Ken Arnold, Part IV":
"JavaSpaces: Data, Decoupling, and Iteration: A Conversation with Ken Arnold, Part V":
"Java Design Issues: A Conversation with Ken Arnold, Part VI":
The Jini Community, the central site for signers of the Jini Sun Community Source License to interact:
Download JavaSpaces from
Design objects for people, not for computers:
"Make Room for JavaSpaces, Part I: An introduction to JavaSpaces, a simple and powerful distributed programming tool":
"Make Room for JavaSpaces, Part II: Build a compute server with JavaSpaces, Jini's coordination service":
"Make Room for JavaSpaces, Part III: Coordinate your Jini applications with JavaSpaces":
"Make Room for JavaSpaces, Part IV: Explore Jini transactions with JavaSpaces":
"Make Room for JavaSpaces, Part V: Make your compute server robust and scalable with Jini and JavaSpaces":
Frank Sommers interview with Jim Waldo, in which Waldo mentions taste:
The Design of Everyday Things, by Donald A. Norman, the book that Ken Arnold suggests to people interested in books on