Simplicity before generality, use before reuse
A common problem we find in component frameworks, class libraries, foundation services, and other infrastructure code is that many are designed to be general purpose without reference to actual systems. This leads to a dizzying array of options and possibilities that are often unused, misused, or just not useful. However, most developers work on specific systems, and the quest for generality does not always serve them well. The best route to generality is through understanding well-defined specific examples and focusing on their essence to find an essential common solution. Simplicity through experience, rather than generality through guesswork.
This maxim, of favoring simplicity before generality, acts as a tiebreaker between otherwise equally viable design alternatives. When there are two possible solutions, favor the one that is simpler and based on concrete need rather than the more intricate one that boasts of generality. Of course, it is entirely possible (and more than a little likely) that the simpler solution will turn out to be the more general one in practice. And if that doesn't turn out to be the case, it will be easier to change the simpler solution to what you now know you need than to change the general one that turns out not to be quite general enough in the right way.
Many things that are designed just to be general purpose often end up satisfying no purpose. A somewhat hyped and optimistic notion of reuse is undoubtedly responsible for many components and frameworks that, with hindsight, turn out to be awkward to use. Software components should, first and foremost, be designed for use, and to fulfill that use well. Designing for all seasons is both difficult and not always desirable, a realization that helps explain the small markets for thermal bikinis and Ford Edsels, as well as the challenge of designing general-purpose software components.
Effective generality comes from understanding, and understanding leads to simplification. Generalization can be used as a cognitive tool, allowing us to reduce a problem to something more essential, resulting in an approach that embodies regularity across known examples, a regularity that is crisp, concise, and well grounded. However, too often generalization becomes a work item in itself, pulling in the opposite direction, adding to the complexity rather than reducing it. The initial sweetness of a general solution can become overwhelming as it grows, to the point that we feel like we are drowning in syrup. The pursuit of speculative generality often leads to solutions that are not anchored in the reality of actual development, making assumptions that later turn out to be wrong, offering choices that later turn out not to be useful, and adding baggage that becomes difficult or impossible to remove, thereby adding to the accidental complexity developers and future architects must face.
Design involves compromise, and flexibility is a double-edged sword. If the flexibility is based on variability found in known examples, the design will be sharper. But if the flexibility is driven by "maybe"s and "what if"s, this is not concrete enough to start adding hooks, options, and other bells and whistles. This kind of speculation is a potentially unbounded set -- "There are more things in heaven and earth, Horatio, than are dreamt of in your philosophy." Features are no longer based on common usage or priority, and so the justification for their proper inclusion is missing. Speculation is a powerful tool in architecture, but for exploration and evaluation, not for embellishment and embodiment.
If we equate flexibility with degrees of freedom, then the degrees of freedom in a design should be reasonable and reasoned. Although many architects value generality, people do not on the whole pay for (or need) it: They tend to have a specific situation, and it is a solution that gives them value. We can find generality and flexibility in trying to deliver specific solutions, but if we weigh anchor and forget the specifics too soon, we end up adrift in a sea of nebulous possibilities, a world of tricky configuration options, overburdened (not just overloaded) parameter lists, long-winded interfaces, and not-quite right abstractions. In pursuit of arbitrary flexibility you can often lose valuable properties, accidental or intended, of alternative, simpler designs.
(Edited RMH 5/28/2008)
This work is licensed under a Creative Commons Attribution 3
Back to 97 Things Every Software Architect Should Know home page