We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Better Software — No Matter What: The Most Important Design Guideline

00:00

Formal Metadata

Title
Better Software — No Matter What: The Most Important Design Guideline
Title of Series
Number of Parts
170
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
At last year’s NDC, Scott Meyers devoted an entire day to guidelines for improving the quality of software, regardless of the application, the language in which it's written, the platform on which it runs, or the users it is intended to serve. This year, by popular demand, Scott isolates the single most important guideline from last year’s talk and focuses on it in this session. The guideline is Make interfaces easy to use correctly and hard to use incorrectly. Scott explains how this applies to both user interfaces and APIs, and, with specific advice and countless examples, shows how to employ it to improve the quality of the many interfaces in your software.
3
Thumbnail
59:06
71
112
127
130
Slide ruleCounterexampleRevision controlComputerComputer iconComponent-based software engineeringTemplate (C++)Modul <Datentyp>Generic programmingSocial classFunction (mathematics)Attribute grammarVariable (mathematics)Logical constantObject (grammar)Parameter (computer programming)Message passingSystem programmingComputer configurationMenu (computing)Integrated development environmentConfiguration spaceConsistencyJava appletLibrary (computing)WeightElement (mathematics)Reflection (mathematics)CodeGUI widgetCategory of beingInheritance (object-oriented programming)Constraint (mathematics)Wrapper (data mining)Limit (category theory)Fluid staticsData typeSign (mathematics)User interfaceSoftware developerSingle-precision floating-point formatSoftwareComputer fileDifferent (Kate Ryan album)Type theoryCompilerOnline helpIntegerInformationNumberInterior (topology)String (computer science)Arithmetic progressionConsistencyGUI widgetConstraint (mathematics)ExistenceComputer configurationInheritance (object-oriented programming)Library (computing)Likelihood functionValidity (statistics)CASE <Informatik>Mathematical analysisPoint (geometry)Expected valueTask (computing)Element (mathematics)WhiteboardFunctional (mathematics)1 (number)Well-formed formulaError messageIntegrated development environmentCodeProcess (computing)Constructor (object-oriented programming)Social classSensitivity analysisWindowDirection (geometry)SpacetimePresentation of a groupDefault (computer science)Series (mathematics)Software development kitRight angleGraphical user interfaceRadio-frequency identificationLevel (video gaming)Multiplication signGraph coloringBinary filePhysical systemGroup actionDistanceOrder (biology)Arithmetic meanResultantComputer programmingKeyboard shortcutAxiom of choiceTable (information)BitRule of inferenceElectronic visual displayMathematicsComputer fontCountingMusical ensembleLine (geometry)Standard deviationControl flowJava appletElectronic mailing listCartesian coordinate systemCounterexampleCategory of beingObject (grammar)Array data structureLengthSelectivity (electronic)MereologySlide ruleNatural numberGeneric programmingForm (programming)NP-hardTerm (mathematics)Row (database)Module (mathematics)Parameter (computer programming)Binary code10 (number)AlgorithmBlogShared memoryVirtual machineQuicksortAsynchronous Transfer ModeSoftware testingWordDomain nameSinc functionSemantics (computer science)Equivalence relationEqualiser (mathematics)Range (statistics)Morley's categoricity theoremOperator (mathematics)Decision theoryTouchscreenIdentical particlesBit rateSheaf (mathematics)CommutatorContent (media)Lattice (order)Programming languageSystem callRevision controlRegulärer Ausdruck <Textverarbeitung>Expert systemSorting algorithmRun time (program lifecycle phase)Ferry CorstenProgrammer (hardware)Pointer (computer programming)Computer programOperating systemPattern languageDemosceneGoodness of fitSet (mathematics)User interfaceFormal languageAliasingReading (process)8 (number)State observerVisualization (computer graphics)Flow separationQuery languageMessage passingComplex (psychology)Regular graphClosed setSound effectMechanism designAreaDevice driverClient (computing)File formatMoment (mathematics)WebsiteMetropolitan area networkMassDrop (liquid)Data recoveryPixelLoginTemplate (C++)Prisoner's dilemmaComplete metric spaceExecution unitOffice suiteOpen setEvent horizonTwitterMultiplicationLink (knot theory)Computing platformSpring (hydrology)Cellular automatonFrustrationForcing (mathematics)Stability theoryCompilation albumNeuroinformatikWeightLetterpress printingAlgebraic closureMiniDiscFreewareSine2 (number)Reflection (mathematics)Physical lawComputer animation
Transcript: English(auto-generated)
All right, well, welcome to the last session of the day, at least the last session of the day for me. I'm amazed that there's this many people at the end of the day, so if you just stumbled in here thinking, well, maybe this is a good place to rest, I'll try not to wake you. My name is Scott Meyers, and I want to talk about what I consider
to be the single most important design guideline in working in software development, and I'll get right to it. Single most important design guideline, in my opinion, is to make interfaces easy to use correctly and hard to use incorrectly. Now, the reason I think it's the single most important design guideline is because this is not a user interface
guideline. This is not an API guideline. This is both. It applies both to people implementing user interfaces, and it also applies to people who are implementing things that are going to be used by programmers, because there are a lot of interfaces in software. In fact, interface design in one form or another is one of the most common activities that we
have as software developers. Now, often when people talk about interfaces, they're thinking about user interfaces, could be a graphical user interface, could be a gesture-based user interface, could be a textual-based user interface. Those are all interfaces. But it also could be the kinds of interfaces that are used by software developers, so
we have library interfaces, we have class interfaces, we have function interfaces, we have module interfaces, we have generic interfaces, we have template interfaces. There are lots and lots of interfaces. So a very common activity by software developers either is designing and implementing interfaces in one form or another, either for end users
or for other developers or for both, or is consuming those interfaces, either as end users or as developers, people using APIs and things like that. It is because interfaces are, by its very nature of the word, they are literally the way that we interact with
part of the system. A well-designed interface can make things go really smoothly, can make it very satisfying, can make it a very pleasant experience, either as an end user or a programmer. And a poorly designed interface, either at the application level or at the user level, can make things really a very miserable existence. Because it is so central
to both using software and to developing software, this is why I think it is the single most important guideline that I know of. So I start as a point of departure with
some assumptions. So the assumptions that I make are first off, the people who are using software, either developers or end users, the first thing is they've used some software before. I think that's a reasonable assumption. They've interacted with some kind of a system before, so they have some idea what should be going on. The second thing is I believe
that they are willing to read at least some documentation, not necessarily a lot of documentation, but they are willing to at least try to follow the rules, and most importantly, I believe that people who interact with interfaces, either as users or as developers, they want to succeed. They want things to work correctly. I do not know many software
developers or many users who say, you know, I'm going to work today, and I'm going to do a really bad job. That's my goal for today is to fail as many times as possible. I just don't think that's very common. So if these things are true, if people have some experience with software, if they're willing to read some documentation to figure
out what the rules are, if they want to succeed, you can't ask them to bring anything more to the table. They're already filling up their end of the bargain, which means if for some reason, despite all these things, they use some software, they use an interface,
and they fail, they do not do what they want to do, it's not their fault. It's their fault for having designed an interface that an experienced person willing to read some documentation who wants to succeed failed to succeed at. That's the sign of a bad interface. So as a result, software should be easy to use correctly, and it
should be hard to use incorrectly. So if it is possible to perform an action, and or do something with a mouse or a keyboard or type in some kind of commands, or call
a function interface, or instantiate a class or a template, these are all acts of interacting with an interface. If it is possible to do something through the interface, it should almost always do what the person who's doing it wants it to. And if somebody could
try to do something which would not yield the desired behavior, then that action should not even be possible. You want to have an interface that fulfills what Rico Mariani has called the pit of success. The pit of success is if you are doing something and
you trip and you fall, you accidentally land doing something that you wanted to do. So if you accidentally succeed with an interface somehow, it means that it just does the right thing, because the things that were not going to do the right thing aren't possible, and the things that are possible almost always do the right thing. That is the goal.
What I want to do for the remainder of this presentation is try to give you more specific ideas on how you can implement easy to use correctly and hard to use incorrectly. I mean, it's one thing to say, well, this is what you should do, but the question is, how do you do it? What are the specific practices that you can adopt that will help make interfaces have this particular characteristic? And the first thing I want to talk about
is adhering to what I call the principle of least astonishment. Everybody who comes to an interface, they have some expectations, they have some background, they have some experience, which means that they have expectations about how that interface is going to work. So your job as an interface designer is to maximize the likelihood that their expectations
are correct. In many cases, they're going to guess at how to use the interface. Your job is to maximize the likelihood that the guesses are going to be correct. And I want to emphasize again, this is not a user interface guideline. This is not an API guideline. This is for both kinds of interfaces. So this should be true across the board.
So if users know what they want to do, then they should be able to figure out how to do it. Now, this doesn't mean there's no innovation in interfaces. So if you come up with some clever new way to let people accomplish a particular task that's better than previous ways of doing it, that's fine. Even if people don't know how to use it initially, once they've learned
how to use it, they should go, oh, now I understand, and their expectations from that point forward should now be something which is going to make it easy to use correctly and hard to use incorrectly. So this does not mean you have to stick with old ideas for interfaces, but it does mean that once you've gotten across the basic ideas, people should be able to figure out how things work.
Now, over the years, I have collected a large number of examples of either interfaces that work or more frequently interfaces that do not work. I'm going to be showing a lot of examples. Some of them will be user interfaces. Some of them are going to be APIs. Some of them are going to be relatively old. Some of them are going to be relatively new.
So in this case here, I just want to talk about some basic ideas of avoiding astonishment. So as an example, a lot of people don't even think about it anymore, and now that we're at Windows 8, maybe it doesn't even work this way, but for many, many years, if you wanted to shut down your Windows computer, you would click on start.
Now, I have actually read the blog entry where they described how they did a lot of user testing and how they finally came up with clicking on start was the right way to shut down the machine. This is stupid. Clicking on start is not the right day to shut down the machine, and I've had to field more than my share of questions from people like my parents saying, so how do I shut down the machine? I say you click on start, and there's dead silence at
the other end of the phone. No one wants to click on start to stop something. I'm sorry, this does not make sense. Now, as I said, I read the blog entry describing how they came up with this, and what it boils down to was we tried a whole bunch of different approaches, and this one sucked the least. Guess what? Sucks least doesn't mean that is the best way to do things.
So many, many years ago, and excuse me, I'm not a Mac user right now, but when I was using Macs from time to time, if you wanted to do things like get a DVD out of the drive or get a CD out of the drive, what you would do is you would drag it to the trash. Who thought this was a good idea? Who wanted to equate destroy this forever with ejecting it?
Now, when I learned about this, which was a number of years ago, quite a number of years ago, I was in graduate school. You don't always trust your colleagues in graduate school. They don't have your best interests at heart. So at one point, I said, okay, how do I get this out of
the machine, and somebody said you drag it to the trash, and I said you are lying to me. It cannot possibly be true. Windows XP, which is going back a while, but there will be newer examples, I promise. Windows XP had the option that you could choose your font size. Now,
there were two selections by the operating system for the font size in Windows XP. Two selections, they ship with Windows XP. There was normal and large fonts. Those were your choices. And what I discovered one time, having tried to use large fonts, is large fonts breaks everything. Large fonts simply does not work, so I've got a couple of
screenshots here, so you can see that, for example, in Excel, you'll notice that the baselines don't line up here. Notice that the name of the band here, this is sponge, but the P and the G are the descenders have been cut off and the CDs cut off on the back here. Basically, nothing displays correctly with large fonts. Call me naive, I was astonished
that using one of two choices for the operating system font size made everything display incorrectly. Now, maybe I'm a little bit too picky, but it seems to me that you shouldn't get 50% of the choices wrong. I mean, there's two choices. And what I found
from talking to Windows developers was it was widespread acknowledgment that it just did not work. I want to point out now that I'm going to be giving lots of different examples. Every single example that I'm going to be giving you is the result of somebody who said,
let's ship this software, this is ready to go. It's easy to pick on things that people throw together in their backyard. Regardless of what you think of Microsoft and Windows, it is a professional software product that a lot of people spent money on. Somebody said, ship that baby, it's ready to go. Everything I'm going to be showing you here is the result
of somebody saying, I now believe this is a professional software product that has the appropriate level of quality and which will satisfy our users. On this slide, we've only the kind of problem that is limited to just user interfaces.
So now let's deal more with a developer level kind of thing. So this happens to be an example from QT 4.8. It's a Windows GUI kit that can be installed. And what it says is the install path must not contain any spaces. Really? On an operating system that by default has
program files and my documents, the installation path can't have any spaces? This is an example of people who develop some software on Unix where spaces are legal but almost never used. And then they said, now we'll port it over to Windows and we will bring our collective Unix baggage with us. And we will then impose it on our users. Now, it goes the
other direction as well. For example, it is not uncommon for people to take Windows software which is not case sensitive and then port it over to Unix and suddenly guess what? Everything starts breaking because it is case sensitive. When you move from one environment to another environment, the expectations that people have are going to change, which means what it means
to make things easy to use correctly and hard to use incorrectly are going to change as well. You can't simply take your conception of what's a good idea and move it someplace else and expect that community of people to adapt to your way of doing things. You have to adapt to their way of doing things. Someone sent me this, and it's so wonderful that I don't think
anybody's ever going to beat this for the worst possible user interface design. Now, this happens to be a calendar program. Oh, it's worse than you think. So this happens to be a calendar program. So if you have a recurring meeting on your calendar, like every
Monday, we have a meeting. And then let's suppose on some Monday you don't want to have the meeting. Maybe you're going to be on vacation. Maybe it's a holiday. Maybe the meeting got cancelled for some other reason. So what you say is great, I want to delete the meeting. Now, when you say that you want to delete the meeting, there's actually some inherent ambiguity here, because you could be saying, I want to delete the meeting forever. We don't
ever need to have it again. It's moved to Tuesdays. Or you could be saying, no, this one particular Monday is the day that it needs to go away. So, all right, there needs to be some way to disambiguate between the two. So as you can see here, it says delete this single item. No to delete all. Now, without even going any further, I have no idea what
that means. So that's no to delete all. And then two choices. You know, I didn't know what it meant in English. And putting it in German is not improving matters any.
But I want to point out, professional software. Somebody shipped this. They thought this would be a great way to satisfy their customers. I call that fairly astonishing. So there are some things you can do to avoid astonishing people. One of the things is you want to
avoid gratuitous incompatibilities with the surrounding environment. People are working in some environment, some kind of a social environment, some kind of a computational environment, some kind of a commute environment. They have expectations. They have normal standard practices. Your goal is to seamlessly work with those things that people are already used to doing. Basically, you want to take advantage of what people already know. That
makes it easier for them to use your software. Again, whether they're end users or whether they're API users, as long as people's habits and expectations correspond to what should be done, you will have a higher rate of success with people doing things correctly. Now, the natural syntax for doing these things can vary depending on the environment.
For example, if I want to find out whether two objects in a programming language have the same value, it's a reasonable thing to want is this thing have the same value as this thing, but the way that you say that depends on the programming environment that you're dealing with. For example, in C++, use operator equal equal. That's what is used in that
programming language. In Java, use equals with a lower case e. In C sharp, use both equals with an upper case e and operator equal equal. So the way that you accomplish the same thing depends on the particular community that you are targeting. So this is why you
need to adapt what you are doing to the community of people who are going to be using it. You also want to offer intuitive semantics. What I normally tell people is if there's not a good reason to diverge from what people are used to, then you want to do what people are used to. In C++ or other languages, when in doubt, do as the ints do. Everybody
knows how an int behaves. If you don't have a good reason for your type to not behave like an int, make it behave like an int. People know what that means. In Gooeys, if you have mouse clicks or gestures, make those things mean whatever they normally mean in other programs that people use. Don't come up with your own special way of doing things unless there's a particular advantage associated with doing that. Again, I don't
want to rule out interface innovation. That's clearly something we need to be able to experiment with. In some cases, people just change things because they can change things and there's no obvious advantage, and that to me is a good way to astonish people. Anybody who is a software developer, anybody who is a programmer, at some point early
in their career, early in their education, they were told you need to choose good names. You have to choose really good names. It's the most important thing, choose really good names. And every single person who has been given this advice has learned that
all the good names were taken in the 1960s. There are no good names left. And yet that's no excuse, because names are, especially for APIs, they literally are the interface. The very first thing people see when they're dealing with an API are things
like class names, module names, function names. The minute they see a word, they are going to go, oh, and an idea is going to spring to mind. And if the idea that springs to mind is not the right idea, you are going to be fighting them every step of the way because they have the wrong conception of what is going on. So it actually is
really important to choose good names. And it's very hard. One of the reasons it's hard is because all the good names were taken in the 60s, but the second thing is there are so many things that have to be named. I mean, choosing one good name is hard. But we have to do things like choose names for libraries, modules, namespaces, generics.
I'm not going to read the whole slide to you, but that's a lot of things that need names. Every one of them is supposed to be a good name. If all you did was pick names all day long, you would go crazy. And yet the fact that it's hard doesn't change the fact that that is literally the interface for any kind of text-based system. The commands that
people type on Unix, which is not setting any records in terms of beauty, the kinds of things people type in APIs, library names, commands to programs, the names are the interface for those kinds of interfaces. And to say things like, well, you know, people will get used to it,
yeah, they will get used to it, but they'll make a lot of mistakes along the way. So it really is important to choose good names. I know it's hard. Doesn't mean it's not something we should all be aspiring to all the time. So here's a couple of counterexamples.
So this one happens to be from Adobe Acrobat 9, but it's standard for the Windows platform. So basically, let's suppose I create a document, I do some edits to it, and then I want to close the program without saving it. So now I have a document that's about to be destroyed without being saved. So the program thinks, all right, we should probably warn you about this. So what it says is, do you want to save the changes to the document? Yes. I know what that means.
No. I know what that means. Cancel. I never have any idea what that means. I have to always think and go, what's the difference between no and cancel again? And I go, all right, cancel means I actually want to cancel the request to close the program. So that doesn't help me too much.
But that's OK. We have interface innovation. So OpenOffice, I downloaded the most recent version of that. OpenOffice not constrained to do things like everybody else does. If you do the same thing in OpenOffice, it asks you the same question. So do you want to save your changes? Well, it doesn't have yes, no, and cancel.
That's too complicated. It has save. Well, I actually knew what yes meant, so that doesn't help me too much. And then it has discard. Well, OK, but I knew what no meant. And then we have cancel. That's what I had trouble with in the any better. Now, there is this notion in the interface community of what is known as the
gulf of execution. The gulf of execution is the distance between conceptually what I wish to be able to do and the means I have to take in order to get it done. The distance between what I want to express and the way that I go about expressing that. And so any time the
somebody is going to do the wrong kind of thing. So, for example, somebody might click on either discard or no when they should have clicked on cancel because they didn't want to lose the document. It is interesting from time to time to think about how we could avoid the problem.
As an example, one could imagine that rather than even presenting this dialogue in the first place, if you try to exit a program and you hadn't saved the document, automatically it would just be saved behind the scenes. You wouldn't have been asked. It would just be there. And then we would have to have some kind of interface that would let people go, oh, right,
I wanted that document. Oh, you saved it for me? That's so nice. Why don't you give it to me? Other interface questions come up, but the point is that there are other ways to approach the problem. For example, rather than saying I'm about to throw your document away, how about don't throw the document away? And then, as I said, there has to be some other kind of interface which would let us get back at those kind of old documents. But by trying to reduce
the Gulf of execution between, oh, I just want to get out of the program, and I'm about to throw a bunch of work away, by adopting a different approach, we can possibly make that tension be lessened. The other thing I would ask about this particular kind of thing, and this is designed for a desktop environment, so this is not designed for a small screen,
I'm trying to figure out why it is in 2014 where we have these giant monitors with huge numbers of pixels, we're still following the convention that we can't have more than one short word on the button. For example, instead of cancel, something like don't exit program.
I bet that would fit. In which case a lot of the confusion about what it means would go away as well. So this is an example of an interface in my mind that was designed in the late 1990s and has not been updated since then. So we've seen too many GUI examples. Let's talk about API examples, and this has to do with choosing good names. Now, in the C++ standard
library, there are two different ways to determine if objects are the same. One of them is called equality, one of them is called equivalence, doesn't matter what the difference is. There's two ways to say is this object equal to this object? There's two ways to say that.
One of them is to use equality and one of them is to use equivalence. So it turns out that they have a function called equal range. Takes a bunch of objects in a range and says, I want you to bunch all of them together whose values are equivalent. So the
name of the function is equal range, but it doesn't use equality. It uses equivalence, and it does make a difference sometimes. And people do get misled into thinking, oh, now I have all the values that are equal to something else, but actually they don't. They have the values that are equivalent to something else. Not always the same. So the question is why did they call it equal range and not equivalent range, since it's not using equality? Another thing you can
do to avoid astonishing people, one of the most important things is you can embrace consistency. You can have consistency in the wording that you use, in the way that you lay things out, in the way you report errors, things like that. So this is an example that was sent to me a while ago. This happens to be from an ATM. And what you will notice in this
ATM is that there's two things you can deposit. You can do a cash deposit and you can do a check deposit. Notice that on the screen, the cash deposit is on the left and the check deposit is on the right. And now notice that underneath where you actually do the depositing,
so the cash deposit is on the right, and the check deposit is on the left. Or, as I like to say, you know, they had a 50% chance of getting it right, which means they had a 100% chance of getting it wrong. But, I mean, this was only rolled out to probably tens of thousands of places around the world. So, really? I mean, is that so complicated to make
sure that things are consistent in terms of their visual, their spatial layout? Also in terms of consistency, in Java, if you would like to find out how many elements are in a container, there's three ways to do it. So if you have an array, use the length property.
If you have a string, use the length method. And if you have a list, use the size method. Three different ways to get the same kind of information. But that's Java. So Microsoft looked at Java and said, man, this is crazy. Three different ways to get the same information. What kind of loser company would have three ways to get the same information?
So when they invented .NET, they said we will not have three ways to get the same information. We'll have two. So if you wonder how many elements are in a container, you have the length property for arrays and you have the count property for array list. Pardon me? I'm sorry,
one more time? On what? On the count method or count property? Okay. All right. So there actually are three different ways. Cool. I'll just make a little note here. You think I'm
making it up. I numerable. Count. Method. Okay. Remember, professional released software products. These are people who are doing the best that they possibly can. But that notwithstanding, so we have a situation now, sometimes when I talk to people, they
say, look, especially in .NET, everybody's using Visual Studio and it's got completion, so all you have to do is type the first letter of the method name or the property name, it will tell you what it happens to be. So my first observation is number one, L and C are not the same letter. So you actually have to find out what that's going to be. The other thing is this makes the assumption that you're always using an IDE.
But these kinds of languages also have reflection-based code. With reflection-based code, you can actually be generating and processing code at runtime. And guess what? At runtime, there's no IDE. Instead, what you have to do is take a look at something and then query its interface to find out what kinds of things it supports. And guess what? Querying becomes a lot more complicated and error-prone if you have to check
for two or three different possibilities. So the excuse of, well, we have an environment that makes these problems go away, bad news. It doesn't make the problems go away. It might reduce the severity in some cases, but it doesn't solve them completely. Again, on the topic of consistency, this is from the C standard library. So we have
three different functions here. We've got fscanf, we have fgetpos, and we have fseek. Notice that all three of them take a file pointer parameter as the first parameter. All right. So we notice a pattern here. When you're dealing with files, you will pass in a file pointer as the first parameter. All right. That's easy to remember.
Oh, wait. It turns out there's also fgets, fputc, and freeopen. It also takes a file pointer as the last parameter. I have talked to several extremely experienced C programmers. People who do this for a living, they've done it for decades.
And they will all say they have to look it up every single time. They simply cannot memorize this stuff. And there's a lovely quote here that has been said. This is something which I think is important to keep in mind. This inconsistency has frustrated millions of developers for more than 30 years. And the reason I point this out is
if you are involved in the development of an interface that is so successful that it is used by millions of people for decades, wouldn't it be nice if one of the things it's remembered for is how easy it was to use and how you didn't have to look up the details every single time you wanted to use it?
Now, most of us will never be sufficiently lucky to develop an interface that has that widespread use. But shouldn't you aspire to something which can stand the test of time and which people are going to look back and say, you know, that was a really nice interface. Got to look it up every single time I want to make a function call. It's kind of crazy.
This is a different kind of consistency. This is from the C++ standard library. Again, I'm trying to mix up API things and GUI things. There's a number of standard containers in the C++ standard library. So let's suppose you have a container and what you want to do is get rid of all the elements of the container that have a particular value.
I want to get rid of all the tens or all the colors that are blue, whatever it happens to be. So if it's a set, you call erase. If it's a multi-set, you call erase. If it's a map, you call erase. If it's an unordered set, unordered map, you call erase. I'm beginning to notice a trend here. If it's a list, you call remove.
And if it's a forward list, you call remove. By the way, they do exactly the same operation, they're just named differently. And what's important to notice about these kinds of examples, the thing about consistency, usually things that are inconsistent, it's an utterly
arbitrary decision. You cannot argue that these names had to be different for some compelling technical reason. They're just different because they probably were developed independently, and then when somebody noticed that they were inconsistent, they didn't bother to fix it. They didn't say no, no, before we standardize this, let's make sure they behave in a consistent
fashion. So some problems have technical justifications. This is not one of them. I've talked about consistency in layout, spatially in a GUI, for example. I've talked about consistency in terms of calling forms. But there's a different kind of
consistency I want to mention now because consistency is across the board. It should occur in many different domains. Again, this is from the C++ standard library. There is a function sort. If you call sort on a collection of values, either it will sort it in N log N time or it will not compile. So, for example,
if you have a doubly linked list which cannot be sorted using this algorithm in N log N time, and you try to sort the doubly linked list, it will not compile. So the philosophy there is if
we can't do this efficiently, we won't even support it. Your program won't compile. That's the philosophy. Now, there is another function. It's called binary search. Binary search in the same standard library, it will run in log N time, which is what you'd
run in linear time. You can invoke binary search on a linked list. This is an inconsistency in philosophy because this says if there's any way to do this, we will do it, even if it's really slow. But what that means is that the library as a whole for your typical developer
working in C++, it means that when they make a function call, they don't know if it's going to compile, and if it does compile, they don't know if it's going to be fast or slow in general, because the people designing the library didn't have a consistent set of philosophies about what it would mean to have performance guarantees. So this is not a syntactic constraint. This is more of a philosophical conceptual inconsistency, and that kind of
inconsistency is no better or worse than any other kind of inconsistency. Another thing from the C++ standard library, and some of you may not know that I sort of live in the C++ world, so that's why some of the examples are coming from that. There is a function called sort.
When you sort values, the sort technique can either be stable or unstable. It doesn't matter what the words mean, but there's two ways to do it, stable or unstable. The sort algorithm is not guaranteed to be stable. But that's okay. If you need stability,
there's another algorithm called stable sort, which is guaranteed to be stable. Well, this makes sense so far. Sort, not guaranteed to be stable. Stable sort, guaranteed to be stable. However, there is also a special algorithm for sorting doubly linked lists. It is called sort, and it's guaranteed to be stable. So we've now talked about
consistency. We've talked about avoiding surprising people. I want to talk a little bit about progressive disclosure. Remember, the high-level goal we're trying to achieve here is interfaces
that are easy to use correctly and hard to use incorrectly. When you get to a particular level of complexity, once there are a whole bunch of choices that people can make, a whole bunch of possible things they can do, the likelihood of them doing the wrong thing
increases. If you're faced with a whole wall of buttons, the chances of hitting the wrong button go up. So what we'd like to do is find a way to reduce the likelihood that people are going to hit the wrong button. And one of the ways to do that is what is known as progressive disclosure. What progressive disclosure does is it says, listen, of all these choices that people have, of all the things that they could do, these are the ones they
probably want to do, and these are the ones that they are much less likely to want to do. Now, we need to make all of them available all the time, but what we can do is make it easy to hit the buttons or use the levers that we want people to use because it's likely to be what they want and harder to use than some other ones. So what you can do is distinguish normal from expert or advanced level kinds of commands.
So this is something that happens to be from the most recent version of Firefox, but you've got some choices here, but notice there's an advanced button, and when you click on the advanced button, it brings up a whole bunch more options. The idea here is that for most people, when they're in, well, what section is this? Content, it looks like. When you're
in the content section here, these are the things that you probably want to work with. But there are some other options for people who are more advanced, in which case they can click on the advanced button and then some more options pop up. The idea is to minimize the likelihood that somebody is just clicking on buttons and gets themselves all messed up by having the user interface designer distinguish between the elementary stuff and the more
advanced stuff. I want to point out that progressive disclosure is not the same as categorizing things. This is from a different program called Super. This is a fairly different program. All the options are still there on one screen. There's nothing there which
says this is probably what you want to do, and this is probably not what you want to do. So there's nothing wrong with categorization. Categorization serves a lot of really useful purposes, but just bear in mind, categorization is not the same as progressive disclosure. Progressive disclosure is a way of hiding things from people initially because they are
unlikely to want to use them. It's a way of making things harder to get at. And it's not something which is limited to user interface design. So if I have an object which has, let's say, 100 methods, number one, I've got a problem already, but if I have
an object with 100 methods, and 20 of them are likely to be used a lot and the other 80 are for very specialized cases, rather than having a single 100-method object, what I can do instead is break it into two objects where one object has the 20 methods that I probably want to use and the other object actually holds the other 80 methods, so I have to
ask for that sub-object in order to get access to those methods. Now, a number of years ago, Ken Arnold, actually almost ten years ago, wrote an article called Programmers are People 2, and it was based on the observation that a lot of work is put into user interface design to make it easier for people to use programs,
but at the underlying programming level, people are often presented with these very large, complicated APIs that are easy to use incorrectly, and his observation was, why don't we do the same kind of thing with programmers that we do with regular end users? Let's also shield them for this kind of complexity. So the example he talked about was Java Swing's JButton class, which offers over 100 methods, but he pointed out, of those
greater than 100 methods, about maybe 15 are the ones that most people dealing with buttons most of the time are likely to want to manipulate, and all the other buttons just get people in trouble and lead to a lot more debugging for their programs, so he proposed a better design which essentially involved taking the JButton class, breaking
it into smaller pieces, and the smaller pieces would then be used as sub-objects, so if you wanted to get access to some of these lesser-used options, you actually would have to ask for a sub-object, which meant that it was less likely that casual users were going to get themselves into trouble. It's progressive disclosure, but it's at the
API level. Another thing you can do to make interfaces easy to use correctly and hard to use incorrectly is simply to document them before you implement them. If you write up the
API before you've actually implemented the interface, you are likely to find problems of the kind we've talked about so far. For example, if things are inconsistent when you start writing about it, you begin to notice that they're inconsistent. If things are overly complicated to try to explain to people, then they probably need to be simplified.
So, if there are bad names, so if a name seems to say one thing but actually says something else, you have a chance to change the name, and this whole idea of writing documentation before you actually write the code is completely consistent, for example, with test-driven development, where you are writing the test cases before you're writing the code itself. But it turns out that the very low-tech approach of documenting
an interface before you've implemented the interface has the nice side effect of making the interface better in most cases. Assuming you are working in a strongly typed programming language, something which actually does enforce typing, if that is a reasonable
assumption, then it's important to understand that the type system is one of the most powerful tools that you have at your disposal. So, by using the type system, you can prevent people from making certain kinds of likely mistakes. So, let's suppose I have a date
class, and here's a date class that takes an integer month, an integer day, and an integer year. Now, one of the reasons I chose the date class is because the conventions for expressing dates, at least colloquially, in the United States are different from the way that they're expressed pretty much every place else in the world. So, there is sort of the inherent possibility of people expressing a date incorrectly, but there's actually a more
general observation I want to make, but the first thing is, so, if we take the int month, the int day, and the int year, then if somebody doesn't pay close attention, they might say, well, I'm going to pass in the day and then the month and the year, or even the year and the month and the day, which arguably is the most reasonable way to do things. But the
interface can't tell the difference. From the interface point of view, they're all just integers, so it has no way of knowing that the wrong things are being passed in. And there is a more generalized observation here. Any time you have an API which has two parameters next to one another with the same type, there is the inherent possibility
that they can be flipped and the type system won't be able to tell. So, any function taking parameters of the same type that are adjacent to one another, you have just eliminated the possibility of the type system being able to tell you when people pass them in the wrong order. But let's talk about this idea of a date class here.
So, this date class here, this is an interface that is easy to use incorrectly. People could easily pass in things that they don't pass in the day, month, and year in the proper order. So, well, okay, what we can do, assuming we have a strong type system, is we can create a type for day, a type for month,
and a type for year. So now they're actually three different types. Well, now that I've got three different types, I can say, okay, here's my date class, you've got to pass in the month first, the day second, and the year third. So now if you do not pass them in the right order, the compiler, through strong type can ensure that your code does not compile. This means we've just eliminated a whole class
of errors. People can't pass these things in in the wrong order. So that's a nice improvement. Now, it also means that the code is clearer. So if I say, all right, I want to have a date D with 4, 8, and 2005, somebody reading that code could easily misinterpret, okay, is that
April 8th or is that August 4th? It's not quite clear from looking at it. When you spell out the types, it's quite clear exactly what's going on. So that's an improvement. So it's easier to read as well as being harder to make mistakes. I want to point out that the technique of introducing new types for strong type checking
only works if you actually are introducing new types. So if you're working in languages with type aliases where you can create another name for an existing type, C and C++ are the that come to mind. If I say, okay, day is a synonym for int, month is a synonym for int, and year is a synonym for int, and here's my date class which takes a month, a day,
and a year, this is a beautiful-looking interface. The problem is as far as the compiler is concerned, it's date of int, int, int. This is what I call programming to make you feel better about yourself. I mean, it looks beautiful, but the problem is you can now pass in a day, a month, and a year, which is the wrong order here, and the code continues
to compile. If you're going to use the type system, you have to use the type system, not the apparent type system. That doesn't do you any good. If you want to know why my fixation is with April 8, 2005, that's the day we brought our puppy home. That's Darla. Darla is adorable, needless to say, but since we're focusing on interfaces,
it's important to recognise, even with what we've talked about so far, there are still ways to make mistakes with this kind of interface. So, what I could do is I could say, okay, I want to have the month M, but the month is minus four. Now, why would you
type minus four as a month? Nobody would type minus four as a month. However, let's suppose you're supposed to be calculating the month, and instead of saying something like A plus B, you accidentally said A minus B. People have been known to make those kinds of mistakes, so you might accidentally compute the month incorrectly. In this particular case, last I
checked, there are only 12 valid months. So if there's only 12 valid months, it doesn't make a lot of sense to me to represent them by probably an integer which could represent four billion possible values. When you have a constrained set of values, in many cases, you are better off designing an interface that constrains the values to only the ones
that are known to be legitimate. So, what we can do is we can say, all right, I've only got 12-month values that make any sense. So I'm going to create 12-month objects, give them the appropriate name, and make it so nobody can create any other month objects. So, for example, I might say here's my class month, and then I've got objects for January,
February, all the way down through December. In this case here, I am making the constructor for months private, which means nobody can create any other month objects. So I've said, here are the 12-month objects that you're permitted to use, and you can't create any more, which means that now it's essentially impossible
to create invalid months. You've eliminated a whole class of errors. So if I try to say month M minus 4, this won't compile because this is trying to create a brand-new month object, and we've made construction illegal for those kinds of things. But I could say the month is month colon colon April. That will work.
So that's nice. That's an improvement. Well, the problem now is, okay, so I could say, all right, the day D is 71. Well, last I checked, there are not any months with 71 days in them. So it's interesting, though, to think about what we can do about dealing with that problem.
I believe it is an extremely useful exercise when looking at any interface to always ask yourself, how could this interface innocently be misused? No one's going to type 71, but, again, a formula might yield something which isn't valid. So the question is, how could we
prevent these kinds of mistakes? Now, what we could do in this particular case is I could say, well, all right, if I know what year it is and I know what month it is, I now know how many days there exist in that particular month. So one could imagine an API where you first you create a year object, you go to the year, you then have it give you month objects,
and once you've got a month, it will then give you day objects, and then it would be impossible to specify the wrong day. Or at least an invalid day. You can specify the wrong day, but you can't specify a day that does not exist. Once you've come up with a possible way to prevent people from making certain kinds of mistakes, then you can say, all right,
what is the likelihood of making the mistake, and how serious will it be if somebody makes it, and how much work is it going to be for me to prevent people from making that mistake? Once you have those two pieces of information, you can start doing an engineering judgment for the trade-offs. You can say, all right, this is how likely it is, and this is what
it's going to cost me if I do it, and this is how much work it is to prevent people from making that kind of mistake. Is it worth it or is it not worth it? I can't tell you the answer to that question, but what I can tell you is in my experience, most people don't do the analysis in the first place. They simply decide, all right, this is what we're going to implement for an API, and we're not going to worry about the kinds of mistakes that people can make.
That's what I just said. Now, constraining values is not always the best way to go. This is from a couple of years ago. Lonely Planet has changed its website, which will
this is an example of an interface that makes the interface designer feel like they're really doing a great thing, they're constraining the values. The problem is they're not making it
impossible to choose illegal values. So this is an example of an interface that looks like it's constraining values for a reasonable case but doesn't solve the problem. What would be a better way to solve this problem? Have a calendar widget so you'd only be able to click
on the appropriate things in the first place. This problem can literally be designed out of existence. What struck me when I did this a couple of years ago, I went to Lonely Planet where if you want to fly, then you go to their flight logger. With the flight, you have a calendar that you choose, and, in fact, there's no drop-downs. On the other hand, if you wanted
to do a hotel, then you had drop-downs, and you had widgets, but if you wanted to rent a car, you only had drop-downs. The reason I bring this up is because, remember, inconsistency is one of the things that makes an interface easy to use incorrectly and harder to use
correctly. This is an example of them having three different approaches to solving the same basic problem. This is from about a year and a half ago, I think. I checked shortly before I came here, and right now, they have a uniform interface. At least they did when I left, but they have right now, I don't know. The last thing I want to mention is to avoid over-reliance on
string. So, if I have an integer in my program, that is almost a meaningless piece of information. That is not a useful type. An int. What does that mean? A street number, an age, number of microseconds since, I don't know, January 1, 1970, number of people attending this
conference, and int is almost useless information. String is similar. If you have a type that is a string, that does not help the compiler help you. So, for example, if I have a file name, I mean, a file name is a completely different kind of thing, for example, from a customer name,
and both of those are really different from, for example, a regular expression. If I have different types for things that are conceptually different, that means that the compiler can make sure I only use them in correct ways. I can do much better validity checking on them. I can also format them for printing things out, stuff like that.
I dealt with a client one time, and they had to deal with printer names and with printer driver names. They were both file names, so they just had a string which represented the name, and they spent a fair amount of unpleasant debugging time trying to figure out why their program was not working because they passed in a driver name when they should have
passed in a printer name because the type system did not realize that these are two completely different kinds of things that are not substitutable to one another. So the summary of what I have to say here is I do believe that the single most important design guideline is to make interfaces easy to use correctly and hard to use incorrectly,
and we talked about some specific ways that you can try to implement that basic idea. One of them is to adhere to the principle of least astonishment, and you can do things like avoid gratuitous incompatibilities with things that are in the surrounding environment, choose good names which are still really hard to choose and still extremely important,
and be consistent in whatever choices you make, whether it's for layout or for naming or for concepts behind things so that people can figure out what you're doing. We talked about progressive disclosure, which is a mechanism that discourages people from
going into the places that will probably get them in trouble and encourages them to stay in the areas where they're probably going to get the results that they want. We talked about the very simple approach of documenting interfaces before we actually implement them, and then under the assumption you are working with a programming language which has some kind of strong typing, talked about introducing new types to prevent errors,
and I emphasize that you have to be types, not type synonyms, so the type system can help you out. Talked about possibly explicitly defining all the values for that type so that people can't create invalid values, and then I concluded by saying you should avoid overreliance on string. If you are interested in more information on any of these kinds of topics,
there are some references here on especially interface and API design, some stuff on user interface design, and that's the end of that. So are there any questions? Yes. What kind of APIs do I like?
So the question is to name an API that I like because I think it's really well designed. Unfortunately, most of my time is spent in the world of C++, and I don't know of any APIs
in that world that I would really hold up and say this is a great example of API design. I wish I had some, but I mean, there are some in that realm that I would say are pretty good, but I wouldn't call them great. So actually, because it's very late in the day, so I'm going to say thank you very much. If you can ask a question later, that would be fine.
So thank you very much for coming, and on your way out, please be sure to pick up little coloured pieces of paper and put them in the bin. Thanks very much.