Code communicates well when a reader can understand it, modify it, or use it. While programming it's tempting to think only of the computer. However, good things happen when I think of others while I program. I get cleaner code that is easier to read, it is more cost-effective, my thinking is clearer, I give myself a fresh perspective, my stress level drops, and I meet some of my social needs. Part of what drew me to programming in the first place was the opportunity to commune with something outside myself. However, I didn't want to deal with sticky, inexplicable, annoying human beings. Programming as if people didn't really exist paled after only a couple of decades. Building ever-more-elaborate sugar castles in my mind became colorless and stale.
One of the early experiences that led me to focus on communication was discovering Knuth's Literate Programming: a progam should read like a book. It should have plot, rhythm, and delightful little turns of phrase. When Ward Cunningham and I first read about literate programs, we decided to try it. We sat down with one of the cleanest pieces of code in the Smalltalk image, the ScrollController, and tried to make it into a story. Hours later we had completely rewritten the code on our way to a reasonable paper. Every time a bit of logic was a little hard to explain, it was easier to rewrite the code than explain why the code was hard to understand. The demands of communication changed our perspective on coding.
There is a sound economic basis for focusing on communication while programming. The majority of the cost of software is incurred after the software has been first deployed. Thinking about my experience of modifying code, I see that I spend much more time reading the existing code than I do writing new code. If I want to make my code cheap, therefore, I should make it easy to read.
Focusing on communication improves thinking by being more realistic. Part of the improvement comes from engaging more of my brain. When I think, "How would someone else see this?" different neurons are firing than when I'm just focused on myself and my computer. I take a step back from my isolated perspective and see my problem and solution anew. Another part of the improvement comes from the reduced stress of knowing that I am taking care of business, doing the right thing. Finally, as a socially oriented species, explicitly accounting for social issues is more realistic than working at pretending they don't exist.
In The Visual Display of Quantitative Information, Edward Tufte has an exercise where he takes a graph and starts erasing all the marks that don't add information. The resulting graph is novel and much easier to understand than the original.
Eliminating excess complexity enables those reading, using, and modifying programs to understand them more quickly. Some of the complexity is essential, accurately reflecting the complexity of the problem to be solved. Some of the complexity, though, represents the claw marks our fingernails make as we struggle to get the program to run at all. It is this excess complexity that removes value from software, both by making the software less likely to run correctly and more difficult to change successfully in the future. Part of programming is to look back at what you've done and separate the wheat from the chaff.
Simplicity is in the eye of the beholder. What is simple to an expert programmer, familiar with the power tools of the craft, might be overwhelmingly complex to a beginner. Just as good prose is written with an audience in mind, so good programs are written with an audience in mind. Challenging your audience a little is fine, but too much complexity will lose them.
Computing advances in waves of complexity and simplification. Mainframe architectures became more and more baroque until mini-computers came along. The mini-computer didn't solve all the problems of a mainframe, but it turned out that for many applications those problems weren't all that important. Programming languages, too, go through waves where they get more complex and then simpler. C begets C++, which begets Java, which is now becoming itself more complicated.
Pursuing simplicity enables innovation. JUnit was much simpler than the testing tools it largely replaced. JUnit spawned a variety of look-alikes, add-ons, and new programming/testing techniques. The latest release, JUnit 4, has lost that "bare metal" feel, although I made or concurred with each of the complexifying decisions. Someday someone will come up with a much simpler way for programmers to write tests than JUnit. The new idea will enable a further wave of innovation.
Apply simplicity at all levels. Format code so no code can be deleted without losing information. Design with no extraneous elements. Challenge requirements to find those that are essential. Eliminating excess complexity illuminates the remaining code, giving you a chance to approach it afresh.
Communication and simplicity often work together. The less excess complexity, the easier a system is to understand. The more you focus on communication, the easier it is to see what complexity can be discarded. Sometimes, however, I find a simplification that would make a program harder to understand. I choose communication over simplicity in these cases. Such situations are rare but usually point to some larger-scale simplification I'm not yet seeing.
Of the three values listed here, flexibility is the justification used for the most ineffective coding and design practices. To retrieve a constant, I've seen programs look up an environment variable containing the name of a directory containing a file in which is found the constant value. Why all the complexity? Flexibility. Programs should be flexible, but only in ways they change. If the constant never changes, all that complexity is cost without benefit.
Since most of the cost of a program will be incurred after it is first deployed, programs should be easy to change. The flexibility I imagine will be needed tomorrow, though, is likely to be not what I need when I change the code. That's why the flexibility of simplicity and extensive tests is more effective than the flexibility offered by speculative design.
Choose patterns that encourage flexibility and bring immediate benefits. For patterns with immediate costs and only deferred benefits, often patience is the best strategy. Put them back in the bag until they are needed. Then you can apply them in precisely the way they are needed.
Flexibility can come at the cost of increased complexity. For instance, user-configurable options provide flexibility but add the complexity of a configuration file and the need to take the options into account when programming. Simplicity can encourage flexibility. In the above example, if you can find a way to eliminate the configurable options without losing value, you will have a program that is easier to change later.
Enhancing the communicability of software also adds to flexibility. The more people who can quickly read, understand, and modify the code, the more options your organization has for future change. The patterns that follow encourage flexibility by helping programmers create simple, understandable applications that can be changed.