Programs share some attributes with essays. For essays, the most important question readers ask is, "What is it about?" For programs, the main question is, "What does it do?" In fact, the purpose should be sufficiently clear that neither question ever needs to be uttered. Still, for both essays and computer code, it's always important to look at how each one is written. Even if the idea itself is good, it will be difficult to transmit to the desired audience if it is difficult to understand. The style in which they are written is just as important as their purpose. Both essays and lines of code are meant—before all else—to be read and understood by human beings.[*]
[*] This chapter was translated from the Japanese by Nevin Thompson.
You may ask: "Are human beings actually supposed to be the ones reading computer programs?" The assumption is that people use programs to tell computers what to do, and computers then use compilers or interpreters to compile and understand the code. At the end of the process, the program is translated into machine language that is normally read only by the CPU. That is, of course, the way things work, but this explanation only describes one aspect of computer programs.
Most programs are not write-once. They are reworked and rewritten again and again in their lives. Bugs must be debugged. Changing requirements and the need for increased functionality mean the program itself may be modified on an ongoing basis. During this process, human beings must be able to read and understand the original code; it is therefore more important by far for humans to be able to understand the program than it is for the computer.
Computers can, of course, deal with complexity without complaint, but this is not the case for human beings. Unreadable code will reduce most people's productivity significantly. On the other hand, easily understandable code will increase it. And we see beauty in such code.
What makes a computer program readable? In other words, what is beautiful code? Although different people have different standards about what a beautiful program might be, judging the attributes of computer code is not simply a matter of aesthetics. Instead, computer programs are judged according to how well they execute their intended tasks. In other words, "beautiful code" is not an abstract virtue that exists independent of its programmers' efforts. Rather, beautiful code is really meant to help the programmer be happy and productive. This is the metric I use to evaluate the beauty of a program.
Brevity is one element that helps make code beautiful. As Paul Graham says, "Succinctness is power." In the vocabulary of programming, brevity is a virtue. Because there is a definite cost involved in scanning code with the human eye, programs should ideally contain no unnecessary information.
For example, when type declarations are unnecessary, or when the design does not require class declarations and main routine definitions, brevity mandates that it should be possible to simply avoid them. To illustrate this principle, Example 29-1 shows a Hello World program in Java and Ruby.
|
|
Both programs accomplish exactly the same task—simply displaying the words "Hello World"—but Java and Ruby approach it in radically different ways. In Ruby's version of the program, all that's necessary is to describe the essence of the task. Print "Hello World". No declaration. No data type. In Java, though, it is necessary to include a variety of descriptions that are not immediately related to our intent. Of course, there is merit in including all of the things that Java does. However, because it is impossible to omit anything, brevity is lost. (To digress a little, Ruby's "Hello World" is trilingual: it also works in Perl and Python.)
Brevity can also mean the elimination of redundancy. Redundancy is defined as the duplication of information. When information is duplicated, the cost of maintaining consistency can be quite high. And because a considerable amount of time can be spent maintaining consistency, redundancy will lower programming productivity.
Although it could be argued that redundancy lowers costs when interpreting meaning, the truth is actually the opposite because redundant code contains so much surplus information. One consequence of this extra weight is that the redundant approach relies on the use of supporting tools. Although relying on IDEs to input information has become popular recently, these tools are not intended to help interpret meaning. The real shortcut for developing elegant code is to choose an elegant programming language. Ruby and other lightweight languages like it support this approach.
In order to eliminate redundancy, we follow the DRY principle: Don't Repeat Yourself. If the same code exists in multiple places, whatever you're trying to say becomes obscured.
The concept of DRY is the antithesis of copy-and-paste coding. In the past, some organizations measured productivity by the number of lines of code a programmer produced, so redundancy was actually tacitly encouraged. I've even heard that copying as much code as possible was sometimes considered a virtue. But this is wrong.
The real virtue, I believe, lies in brevity. The recent popularity of Ruby on Rails is driven by its dogged pursuit of brevity and DRY. The Ruby language is serious about "never writing the same thing twice" and "making descriptions concise." Rails inherits this philosophy from the Ruby language.
A more controversial aspect of beautiful code may be its familiarity. Human beings are more conservative than you might think; most people find it difficult to embrace new concepts or change their ways of thinking. Instead, many prefer to continue suffering rather than change. Most people are unwilling to replace familiar tools or learn a new language without a good reason. Whenever they can, human beings will compare new processes they are trying to learn with what they have always regarded as common sense, with a resulting negative assessment of the new process that may be undeserved.
The cost of changing one's ways of thinking is far higher than is commonly thought. In order to easily switch between totally different concepts (for example, from procedural programming to logical or functional programming), it is necessary to become acquainted with a wide variety of concepts. Steep learning curves create pain in humans' brains. Therefore, they reduce programmers' productivity.
According to this point of view, because Ruby supports the concept of "beautiful code," it is an extremely conservative programming language. While called a pure object-oriented language, Ruby does not use innovative control structures based on object message passing like Smalltalk. Instead, Ruby sticks to traditional control structures programmers are familiar with, such as if, while, etc. It even inherits the end keyword to terminate code blocks from good old Algol-family languages.
Compared to other contemporary programming languages, Ruby does sometimes look old-fashioned. But it's important to keep in mind that "never be too innovative" is also a key to creating beautiful code.
Simplicity is the next element of beautiful code. We often feel beauty in simple code. If a program is hard to understand, it cannot be considered beautiful. And when programs are obscure rather than comprehensible, the results are bugs, mistakes, and confusion.
Simplicity is one of most misunderstood concepts in programming. People who design languages frequently want to keep those languages simple and clean. While the sentiment is noble, doing this can make programs written in that language more complex. Mike Cowlishaw, who designed the Rexx scripting language at IBM, once pointed out that because language users are more common than language implementers, the needs of the latter must give way to those of the former:
The general principle is that very few people have to implement interpreters or compilers for a language, whereas millions of people have to use and live with the language. One should therefore optimize for the millions, rather than the few. Compiler writers didn't love me for that, because Rexx got to be a hard language to interpret or compile, but I think it has paid off for people in general, certainly programmers in general. [
]
[
] Dr. Dobb's Journal, March 1996.
I agree from the bottom of my heart. Ruby is meant to be the personification of this ideal, and while it is far from simple, it supports simple solutions for programming. Because Ruby is not simple, the programs that use it can be. This is true of other lightweight languages as well; they are not lightweight in the sense of ease of implementation, but they are called lightweight because of their intention to lighten the workload of the programmer.
To see what this means in practice, consider Rake, a build tool like make that is widely used by Ruby programmers. Unlike Makefiles, which are written in a single-purpose file format, Rakefiles are written in Ruby, as sort of Domain Specific Language (DSL) with full-featured programmability. Example 29-2 shows a Rakefile that runs a set of tests.
task :default => [:test] task :test do ruby "test/unittest.rb" end |
The Rakefile takes advantage of the following shortcuts in Ruby syntax:
Parentheses for method arguments can be eliminated.
Unbraced hash key/value pairs can appear at the end of methods.
Code blocks can be attached at the tails of method calls.
You can program in Ruby without these syntax elements, so in theory, they are redundant. They are often criticized for making language more complex. However, Example 29-3 shows how Example 29-2 would be written without using these features.
task({:default => [:test]})
task(:test, &lambda(){
ruby "test/unittest.rb"
})
|
As you can see, if Ruby's syntax were stripped of redundancies, Ruby the language might become more elegant, but programmers would have to do more work, and their programs would be harder to read. So, when simpler tools are used to solve a complex problem, complexity is merely shifted to the programmer, which is really putting the cart before the horse.
The next important element in the concept of "beautiful code" is flexibility. I define flexibility here as freedom from enforcement from tools. When programmers are forced to do something against their intentions, for the tools' sake, the result is stress. This stress negatively affects the programmer. The end result is far from happiness, and far from beauty as well, according to our definitions of beauty in code. Humans are more valuable than any tools or languages. Computers should serve programmers to maximize their productivity and happiness, but in reality, they often increase the burden instead of lightening it.
Balance is the final element of beautiful code. So far I have talked about brevity, conservatism, simplicity, and flexibility. No element by itself will ensure a beautiful program. When balanced together and kept in mind from the very beginning, each element will work harmoniously with the others to create beautiful code. And if you also make sure to have fun writing and reading code, you will experience happiness as a programmer.