Sure It Works, But Is It Beautiful?
Examples of software systems that cooperate with their environment include:
A more technical example is a large-scale document management system that I worked on. The software architect cleverly designed the storage and retrieval methods to take into account the operating properties of the computer's disk drive, so that the whole system worked as efficiently as possible. In all of these cases, the software is working in cooperation with its environment -- just as a well-designed building works in harmony with its site.
The internal design of a software system should reflect and create its external functions. Beautiful buildings merge form and function, and good (beautiful) software does the same. Why does this matter though? As long as a software system works correctly, does it matter what internal design achieves that end? It does matter. An internal structure that acknowledges the external features is more likely to create those features correctly. A software form that follows the software's function also is likely to be simpler, since the external features arise from the internal design rather than being forced on top of the design.
Nearly all current software development is planned and completed with no concern for the marriage of form and function. Software managers and executives are grateful when a project is finished on time, and are not worried about what the inside of the software looks like. The problem is that the inside of the software has a tremendous impact on whether the software actually will be useful. A software system whose form does not mirror its function forever will be difficult to debug, will have more bugs, will be difficult to extend and modify, and likely will perform its core functions poorly.
Since we cannot see or touch software, it is sometimes difficult to judge whether a software system's form matches its function. All software has a definite form however. Consider an accounting system. Business accounting consists of several well-defined operations: purchasing, billing, payroll, general ledger, etc. To a large extent these functions are understood on their own, but there also is some overlap among them. A software system for business accounting had better reflect these logical accounting operations in its internal design. There should be clearly defined parts of the software for purchasing, billing, payroll, etc. There also should be clear overlap in the software where the logical operations overlap. Without such a software design, it will be impossible to change just one aspect of payroll without affecting other operations. Or to make a change in an area that overlaps (such as general ledger) and have it correctly change all related operations.
Software design that reflects external function implies that there are no fixed rules for good programming technique. For many years, programmers have been taught that global variables and GOTO statements are poor programming practice. In some situations though, these constructs may be exactly what the software needs to marry form with function. It would be incorrect to propose the rule of building construction: "Always use teak wood instead of pine." Teak is an excellent wood, but sometimes pine is the right choice. In the same way, the right question about a programming technique is: "Is this an appropriate design for the external features we are trying to achieve?"
Imagine a house built on a street that contains public water supply and electrical service. Now suppose that the builders of the house dig a private well and construct their own power generating station. On asking them why they did this, they reply, "We wanted our own well, so it would be just they way we like it. And we didn't know there was electricity on the street." This extra work and expense in building a house would be unacceptable. There is nothing wrong with including a well or power generation at a house -- if the home needs them. It is horrible design to include them for little reason or out of ignorance about the public services. Good building design keeps the building as small as possible, by using available external resources.
Beautiful software follows the same principle -- it is as small as it can be, by using existing computing resources where possible. It is the responsibility of every software architect and engineer to understand the computing system they are using, and to take advantage of its facilities whenever possible. Software should contain just what it needs to, and no more.
Some years ago, I worked on a project that exemplified everything that can be wrong with software in this regard. The project was a financial information system on Digital's VAX minicomputers. In dozens of places throughout the code we chose to "roll our own" method of doing something, rather than use a facility built into the VAX. We wrote our own sorting procedures, screen input/output package, source code control, and automated build tools -- all of which were provided on the VAX. I am embarrassed to admit that we even wrote our own run-time debugger -- although the VAX contained an excellent one. In each case our reason was hubris, ignorance, or laziness to learn more about the computer we were using. The resulting system was many times larger than it needed to be, ran slowly, took a long time to create, and cost far too much money . (I hope I have learned something since then.)
In general, beautiful buildings contain one room for each purpose, and that room gets the function correct. For example, in most houses there is one master bedroom, which contains everything the room needs. It would be exceedingly poor design for a house to contain four master bedrooms because the builders could not make any of them complete. Or worse, if the builders forgot that they had already created a master bedroom and built another one, then forgot about the second and built another, etc.
Of course, some large buildings may require more than one space of each type. For example, an office building with 5000 workers may need more than one cafeteria. It is poor architecture, however, unnecessarily to duplicate a space.
In the same way, well designed software generally contains one instance of each component, and makes that component work correctly. The opposite of this is redundancy and is well recognized as poor software architecture. To cite a specific example, imagine a software system that contains three drivers for one printer. Any change to the printer would require changes to three pieces of software. It is remarkable, however, how many software system contain unnecessary redundant code. In many cases, it is because one programmer does not know that another engineer already solved the same problem. (The builders forgot they already had a master bedroom.)
Good building design places related items together. The equipment and supplies for preparing meals usually are in one room. Plumbing, electrical and heating devices often are together in the basement. The coat closet is near the front door. Note that houses do not have to be designed this way. A house could be built with a refrigerator in the attic, an oven in the living room, and a dishwasher in the bedroom -- but it would be poor design to do so.
Good software design follows the same principal of placing related items together. When software is constructed this way, it is easy for members of the project team to understand the software -- because the structure makes sense. It is easy to fix bugs and make changes -- because the relevant code is located in an obvious place. It is easier to replace an entire feature with a new approach -- because there is one distinct piece to replace.
Functional locality implies levels of abstraction. Each room in a house has a purpose, with related items for that purpose in the room. At a broader view though, good building architecture places related rooms together. Rooms for daytime use often are at one end of a house (or on one floor). Rooms for nighttime use are at the other end of the house or upstairs. Office buildings place mechanical infrastructure rooms in one area, away from business people. Good software architecture also uses levels of abstraction to achieve functional locality. In an operating system, all the low-level code for audio effects (sound) should be in one place. Within each higher-level feature (such as the file system), the code for those sound effects should be together. Moving up a level, the code for related features (such as the file system and Internet explorer in Windows 98) should be together.
Software should do its work and solves its problems in the simplest manner possible. In many ways, simplicity is the most important principle of all and overlaps with all the other principles. Simple software is beautiful. Beautiful software is simple. Simple programs have fewer bugs (because there are fewer lines of code that can be wrong), run faster (because there are fewer machine instructions), are smaller (because there is less compiled code), and are easier to fix when broken (because there are fewer places where a bug can occur). Simple programs are dramatically less expensive to create and maintain, for all of the above reasons.
The simplicity of software also is a key metric that distinguishes programming ability. Junior programmers create simple solutions to simple problems. Senior programmers create complex solutions to complex problems. Great programmers find simple solutions to complex problems. The code written by topnotch programmers may appear obvious, once it is finished, but it is vastly more difficult to create. Just as the goal of science is to find simplicity and order in a seemingly complex universe, the goal of programming should be to find simple solutions to complex problems.
Putting It Together
Finally, there is a global quality to beautiful software that is not the sum of the previous attributes. All of the above qualities must come together to create an overall design that is beautiful. Such beauty cannot be obtained by following a rigid set of guidelines based on the above principals -- even though each is important. Just as house architects cannot design beautiful buildings simply by including known elements that have worked elsewhere, good software design is more than a collection of programming techniques that make sense on their own. Beautiful software is achieved by creating a wonderful whole that is more than the sum of its parts. Beautiful software is the right solution, both internally and externally, to the problem presented to its designers.
The result of good software design is a program that is better by many measures; it is not just better in the abstract. Compared to a poorly designed solution, well designed software meets users' needs more closely, can be completed more quickly, works more reliably, and costs far less money initially and throughout its life. All of us in the software world have accepted poor software architecture too often. We should demand the same level of quality in software systems as we do in the buildings where we live and work.
Biography: Charles Connell is president of CHC-3 Consulting, teaches computer science at Boston University, and writes frequently on computer topics.
short version of this article appeared in the Boston Globe on July 6, 1999.)
Copyright 2000 by Charles H. Connell Jr.