34.1. Conquer Complexity

 < Free Open Study > 

The drive to reduce complexity is at the heart of software development, to such a degree that Chapter 5, "Design in Construction," described managing complexity as Software's Primary Technical Imperative. Although it's tempting to try to be a hero and deal with computer-science problems at all levels, no one's brain is really capable of spanning nine orders of magnitude of detail. Computer science and software engineering have developed many intellectual tools for handling such complexity, and discussions of other topics in this book have brushed up against several of them:

Cross-Reference

For details on the importance of attitude in conquering complexity, see Section 33.2, "Intelligence and Humility."


  • Dividing a system into subsystems at the architecture level so that your brain can focus on a smaller amount of the system at one time.

  • Carefully defining class interfaces so that you can ignore the internal workings of the class.

  • Preserving the abstraction represented by the class interface so that your brain doesn't have to remember arbitrary details.

  • Avoiding global data, because global data vastly increases the percentage of the code you need to juggle in your brain at any one time.

  • Avoiding deep inheritance hierarchies because they are intellectually demanding.

  • Avoiding deep nesting of loops and conditionals because they can be replaced by simpler control structures that burn up fewer gray cells.

  • Avoiding gotos because they introduce nonlinearity that has been found to be difficult for most people to follow.

  • Carefully defining your approach to error handling rather than using an arbitrary proliferation of different error-handling techniques.

  • Being systematic about the use of the built-in exception mechanism, which can become a nonlinear control structure that's about as hard to understand as gotos if not used with discipline.

  • Not allowing classes to grow into monster classes that amount to whole programs in themselves.

  • Keeping routines short.

  • Using clear, self-explanatory variable names so that your brain doesn't have to waste cycles remembering details like "i stands for the account index, and j stands for the customer index, or was it the other way around?"

  • Minimizing the number of parameters passed to a routine, or, more important, passing only the parameters needed to preserve the routine interface's abstraction.

  • Using conventions to spare your brain the challenge of remembering arbitrary, accidental differences between different sections of code.

  • In general, attacking what Chapter 5 describes as "accidental difficulties" wherever possible.

When you put a complicated test into a boolean function and abstract the purpose of the test, you make the code less complex. When you substitute a table lookup for a complicated chain of logic, you do the same thing. When you create a well-defined, consistent class interface, you eliminate the need to worry about implementation details of the class and you simplify your job overall.

The point of having coding conventions is also mainly to reduce complexity. When you can standardize decisions about formatting, loops, variable names, modeling notations, and so on, you release mental resources that you need to focus on more challenging aspects of the programming problem. One reason coding conventions are so controversial is that choices among the options have some limited aesthetic base but are essentially arbitrary. People have the most heated arguments over their smallest differences. Conventions are the most useful when they spare you the trouble of making and defending arbitrary decisions. They're less valuable when they impose restrictions in more meaningful areas.

Abstraction in its various forms is a particularly powerful tool for managing complexity. Programming has advanced largely through increasing the abstractness of program components. Fred Brooks argues that the biggest single gain ever made in computer science was in the jump from machine language to higher-level languages it freed programmers from worrying about the detailed quirks of individual pieces of hardware and allowed them to focus on programming (Brooks 1995). The idea of routines was another big step, followed by classes and packages.

Naming variables functionally, for the "what" of the problem rather than the "how" of the implementation-level solution, increases the level of abstraction. If you say, "OK, I'm popping the stack and that means that I'm getting the most recent employee," abstraction can save you the mental step "I'm popping the stack." You simply say, "I'm getting the most recent employee." This is a small gain, but when you're trying to reduce a range in complexity of 1 to 109, every step counts. Using named constants rather than literals also increases the level of abstraction. Object-oriented programming provides a level of abstraction that applies to algorithms and data at the same time, a kind of abstraction that functional decomposition alone didn't provide.

In summary, a primary goal of software design and construction is conquering complexity. The motivation behind many programming practices is to reduce a program's complexity, and reducing complexity is arguably the most important key to being an effective programmer.

 < Free Open Study > 


Code Complete
Code Complete: A Practical Handbook of Software Construction, Second Edition
ISBN: 0735619670
EAN: 2147483647
Year: 2003
Pages: 334

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net