Chapter 6. Simplicity

Okay, so if we never change our software, we can entirely avoid defects. But change is inevitable, particularly if we’re going to add new features. So “don’t change anything” can’t be the ultimate defect reduction technique.

As explained in Chapter 5, if you want to avoid defects in your code, it does help to keep your changes small. But if you want to go further and eliminate defects even from your small changes, there’s another law that can help you. And it doesn’t just reduce defects—it keeps your code maintainable, makes it easy to add new features, and improves the overall understandability of your code. This is the Law of Simplicity:

That is, the simpler the pieces are, the more easily you can change things in the future. Perfect ease of maintenance is impossible, but it’s the goal you strive for—total change or infinite new code with no difficulty.

You may have noticed that this law doesn’t talk about the simplicity of the whole system, though, only the individual pieces. Why?

Well, an average-sized computer program is so complex that no human being could comprehend it all at once. It’s only possible to comprehend pieces of it. So we actually always have some large, complex structure for our whole program. What then becomes important is that the pieces can be understood when we look at them. The simpler the pieces are, the more likely it is that any given person will understand them. That’s particularly important when you’re handing your code off to other people, or when you go away from your code for a few months and then have to come back and relearn what you did.

So how do we use this law, in the practical world of programming? That’s the subject of much of the rest of this book. In general, though, the idea is to make the individual components of your code as simple as possible, and then make sure they stay that way over time.

One good way to do this is to use the incremental development and design method introduced at the end of Chapter 4. Since there is a “redesign” step before each new feature is added, you can use that time to simplify the system. Even if you’re not using that method, though, you can take some time between adding features to simplify any pieces that seem too complex to you or your fellow developers.

One way or another, you often have to take what you’ve created and make it simpler—you can’t rely on your initial design always being the right one. You have to redesign pieces of the system continuously as new situations and requirements arise.

Granted, this can be a fairly difficult task. You aren’t always given simple tools to write your programs with—the languages are complex, the computer itself is complex, etc. But strive for simplicity with what you have.

You may have realized this, but this law tells us the most important thing we can do right now that will reduce the effort of maintenance in the Equation of Software Design—make our code simpler. We don’t have to predict the future to do that; we can just look at our code, see if it is complex, and make it less complex for ourselves right now. This is how you get an effort of maintenance that decreases over time—you continually work to make your code simpler.

There is a certain amount of work involved in doing this simplification, but overall it is far easier to make changes in a simple system than in a complex system—so you spend a little time doing the simplification now to save a lot of time later.

As you decrease the effort of maintenance for your system, you increase the desirability of all possible changes. (Go back to Chapter 3 and take another look at the Equation of Software Design if you want to refresh your memory about the details.) Simplifying your code decreases the effort of maintenance, thereby increasing the desirability of every other possible change.

Okay, so we want things to be simple. However, how you define “simple” really depends on your target audience. What is simple to you might not be simple to your coworkers. Also, when you create something, it may seem relatively “simple” to you, because you understand it inside and out. But to somebody who’s never seen it before, it might appear very complicated.

If you want to understand the viewpoint of somebody who doesn’t know anything about your code, find some code you’ve never read, and read it. Try to understand not just the individual lines, but what the whole program is doing and how you would modify it if you had to. That’s the same experience other people are having when reading your code. You might notice that the level of complexity doesn’t have to get very high before it becomes frustrating to read other people’s code.

That’s why it’s good to have sections in your code documentation like “New to This Code?” that contain some simple explanations that will help people understand your code. These should be written as if the reader knows nothing about the program, because if people are new to something, they probably don’t know anything about it.

Way too many software projects mess this up. You go to read the documentation written for developers, and you’re presented with a huge mass of links and no direction. This appears simple to the long-time developer of the project, because a page with lots of links lets that developer quickly go to the part he’s looking for. But for someone new to the project, it’s complicated. On the other hand, for the long-time developer, adding a page with big, simple buttons and eliminating that list of links would add to the complexity of his task, because his main goal will just be to find a very specific thing very fast in the documentation.

The only thing worse than complex documentation is no documentation, where you’re just expected to figure it out for yourself or “already know” how the code works. To the developer, the way his program works is obvious, but to others it’s totally unknown.

Context is important, too. For example, in the context of program code, advanced technologies often lead to simplicity, if used right. But imagine if such a program’s advanced internal structure were displayed directly on a web page as the only interface to the program—it wouldn’t be simple in that context, even to the developer!

Sometimes what seems complex in one context is simple in another. Displaying a lot of explanatory text on a billboard by the side of the road would be overly complex—there’s just no time for passing drivers to read all that text, so it would be stupid to put it there. But in a manual for a computer program, including lots of explanatory text would be a lot simpler than just giving a one-sentence description of something. That’s why this book doesn’t have just one-line chapters; it wouldn’t really be all that simple to just say something and then not explain it.

With all these different viewpoints and contexts to consider, does this mean that achieving simplicity is impossibly difficult? No! Not at all. There are specific target audiences for everything, and the context of any individual thing you’re doing is usually pretty limited. The problem is always solvable. It’s just important to take these considerations into account when designing your software, so that when someone actually comes to use it, it really is simple for that particular person.

When you’re working on a project, questions about simplicity can arise. How simple do we really have to be? Just how much do we have to simplify this thing? Is it simple enough?

Well, of course, simplicity is relative. But even so, you can still achieve more or less simplicity. From the relative viewpoint of your user, your product can be hard to use, easy to use, or somewhere in between. Likewise, from the viewpoint of another programmer, your code can be relatively hard or easy to read.

So, how simple do you have to be?

Honestly?

If you really want to succeed?

Stupid, dumb simple.

The nice thing about that level of simplicity is that, for the most part, anything usable by normal people is also usable by geniuses. You get a much broader range of possible users.

But often, people really just don’t understand how stupid, dumb simple they have to be to get to that level. Let’s look at an example. When you’re at the mall, there are maps that tell you where everything is. On the best mall maps, there is a huge red dot, with the words “YOU ARE HERE” in gigantic letters, right in front of you. On the poorer maps, there is a tiny yellow triangle in the middle of the map that is very hard to find, and off to the side there’s some text that explains, “The tiny yellow triangle means ‘You are here!’” Add this to the general confusion of trying to find anything on these maps, and you could be spending five or six minutes just standing in front of the thing, trying to figure out how to get where you’re going.

To the guy that designed the map, this may seem totally reasonable. He spent lots of time designing it, so it was clearly important enough to him that he would be happy to spend several minutes looking at it, learning all about it, figuring it out, etc. But to us, the people who are actually using the map, it’s a very, very minor part of our existence. We just want it to be as simple as possible, so that we can use it quickly and get on with our lives!

Many programmers are particularly bad about this with their code. They assume that other programmers will be willing spend a lot of time learning all about their code, because after all, it took a lot of time to write it! The code is important to them, so won’t it be important to everybody?

Now, programmers are generally an intelligent bunch. But it’s still a mistake to think, “Oh, other programmers will understand everything I’ve done here without any simplification or explanation of my code.” It’s not a matter of intelligence—it’s a matter of knowledge. Programmers who are new to your code don’t know anything about it; they have to learn. The easier you make it for them to learn, the faster they are going to figure it out, and the easier it will be for them to use it.

There are lots of ways to make your code easy to learn: simple documentation, simple design, step-by-step tutorials, etc.

But, if your code isn’t stupid, dumb simple to learn, people are going to have trouble with it. They’re going to use it incorrectly, create bugs, and generally muck things up. And when all this happens, who are they going to come ask about it? Yes, you! You are going to be spending time answering all of their questions. (Mmm, sounds fun, doesn’t it?)

None of us like being talked down to or treated like we’re idiots. And sometimes that leads us to create things that are a little complicated, so that we feel like we aren’t talking down to the user or to other programmers. We throw in some big words, make it a little less than simple, and people respect our intelligence but feel kind of stupid because they don’t get it. They might think we’re way smarter than they could ever be, and that is kind of flattering. But really, is that helping them?

On the other hand, when you make your product or code stupidly simple, you’re allowing people to understand it. That makes them feel smart, lets them do what they’re trying to do, and doesn’t reflect badly on you at all. In fact, people will probably admire you more if you make things simple than if you make them complex.

Now, your whole family does not have to be able to read your code. Simplicity is still relative, and the target audience for code is other programmers. But to those other programmers, your code should seem very simple and easy to understand. It can use as much advanced technology as is required to achieve that simplicity, but it should still ultimately be simple.

When the question “How simple do I have to be?” comes up, you might as well ask yourself, “Do I want people to understand this and be happy, or do I want them to be confused and frustrated?” If you pick the former, there’s only one level of simplicity that will assure your success: Stupid, dumb simple.

Consistency is a big part of simplicity. If you do something one way in one place, do it that way in every place.

If you name a variable somethingLikeThis, then all of your variables should be named that way (otherVariable, anotherNameLikeThat, etc.). If you have variables that are named_like_this, then all variables should be all lowercase and have underscores between the words.

Code that isn’t consistent is harder for a programmer to understand and read.

We can illustrate this by looking at an example from natural language. Compare these two sentences:

Both of those sentences say the exact same thing, but the first one is way simpler to read because it’s consistent with how most people write English. Sure, it is possible to read the second sentence, but would you want to read a whole book written like that? Right. So, would you want to read a whole program written without any consistency?

There are situations in programming where it doesn’t matter how you do things, as long as you always do them that way. Theoretically, you could write your code in some crazy complex way, but as long as you were consistent with it, people would learn how to read it. (Of course, it’s better to be consistent and simple, but if you can’t be totally simple, at least be consistent.)

Total consistency can also make programming easier in many cases. For example, if every object in your program has a field called name, you can write one piece of simple code that deals with the name field of every object in your entire program. But if in Object A the name field is called a_name and in Object B it’s called name_of_mine, you’ll have to write special code to deal with Object A and Object B differently.

Similarly, your program should behave in a consistent fashion internally. A programmer who is familiar with how to use one part of your code should be immediately familiar with how to use another part of your code, because both pieces behave in a similar fashion. For example, if when using Part A the programmer has to call three functions and then write some code, when using Part B she should also have to call a similar set of three functions and then write some code. And if you have a function named dump in Part A that causes Part A to print out all its internal variables, the function named dump in Part B should do the same thing for Part B. Don’t keep forcing programmers to relearn the way your system works every time they look at a new piece of it.

Maybe things aren’t that consistent in the real world, but you’re in charge of the world of your program, so you can make things simple and consistent.

There are some examples of consistency in the real world. In much of Asia, people use chopsticks to eat. In the Americas and Europe, people use forks. Okay, that’s two different methods of eating, but overall it’s pretty consistent, in any given area. Now imagine if every time you went to somebody’s house, you had to learn some whole new way of eating. Maybe at Bob’s house they eat with scissors, and at Mary’s house they eat with flat pieces of cardboard. Eating would get pretty complex, wouldn’t it?

It’s the same in programming—without consistency, things get very complex. With consistency, they become simple. And even if they’re not simple, at least you can learn the complexity just once, and then you know it forever.

As has been said many times in the world of software development, code is read more often than it is written. So, it’s important to make code easy to read:

If the whole universe were black, you wouldn’t be able to tell objects apart. They’d all be a single black mass. Just the same, if a whole file is a mass of code without enough consistent, logical spacing, it’s hard to separate out the pieces. Space is what keeps things separate.

You don’t want too much space, because then it’s hard to tell how things are related. And you don’t want too little, because then it’s hard to tell that things are separate.

There’s no hard and fast rule about exactly how code should be spaced, except that it should be done in a consistent manner and the spacing should help inform the reader about the code’s structure.

An important part of readability is giving good names to variables, functions, classes, etc. Ideally:

It’s also important to think about how the function, variable, etc. is going to be used. Once we start putting its name into lines of code, will it make those lines of code so long that they’re hard to read? For example, if you have a function that is only called once, on one line all by itself (with no other code in that line), it can have a fairly long name. However, a function that you’re going to use frequently in complex expressions should probably have a name that is short (though still long enough to fully communicate what it does).

Unfortunately, people do not naturally build simple systems. Without attention paid to design, a system will evolve into a massive, complicated beast.

If your project lacks a good design, and it continues to grow, you will eventually end up over your head in complexity. This is hard for certain people to imagine—some can’t imagine that there is a future beyond lunch, and others just haven’t had enough experience to understand how complex things can get. And there can be a corporate culture that says, “Oh, we just hack in new features; we should do things the right way, but we can’t because blah blah blah.” But one day, your project will fail. And no matter how many reasons you can give for that failure, it won’t change the fact that your project failed.

On the other side of things, when you’ve designed well, there’s often not a whole lot of credit that comes your way. Catastrophic failures in design are big and noticeable, whereas small increments of work toward a good design are invisible to people who aren’t intimately connected with the code. This can make being a designer a difficult job. Handling a big failure gets you a lot of thanks, but preventing one from ever happening...well, nobody’s likely to notice.

So, let’s congratulate you here. Did you think a bit about design? Great! Your users and fellow developers will see the benefits—working software, on-time releases, and a clear, understandable codebase. You will feel confident in your own work and go home feeling accomplished. Will the other developers know how much work it took to make things run so smoothly? Maybe not. But that’s okay. There are other rewards in the world besides the congratulations of your peers.

Once in a rare while, though, you will get some appreciation for all of your work. Don’t despair—somebody will notice eventually. And until then, enjoy all of the other positive results of effective, correct design.