Structural simplicity.


You've all seen Rich Hickey's excellent Simple-made-easy presentation, in which Mr Hickey trawls the dictionary to remind us that, "Simple," doesn't mean ease-of-use or intellectually un-challenging but, "Not complex or compound."

The opposite of simple is not, "Hard," but, "Composition." If a piece of software is simple then it is not composed of other pieces of software. We all know, however, that software is most certainly composed of other pieces of software. So is simplicity impossible?

Consider the trivial system of two methods in figure 1, in which method a() depends on method b().

Figure 1: A system of two methods - image by Spoiklin Soice

Figure 1: A system of two methods.

When viewed in purely structural terms, composition appears as elements connected via dependencies. In figure 1, we can say that method a() is composed of method b() because a() cannot perform its task without b(). So a() is a structurally composed method. Method b(), however, depends on nothing else, so we say that it is structurally simple. In reality, b() may be a thousand-line monster performing countless tasks, each composed of other parts (loops, switch statements, etc.) of that same method, but from our method-level structural vantage point it stands self-contained. Hence the qualifying, "Structurally," of, "Structurally simple."

Composition, furthermore, would seem transitive. Consider the slightly enlarged system of figure 2.

Figure 2: A system of many methods - image by Spoiklin Soice

Figure 2: A system of many methods.

In figure 2, a() now depends on c() and b(), and b() itself depends on d(), e() and f(). It appears that a() must in some sense also be composed of d(), e() and f() because it cannot function correctly unless those three methods do.

The drive towards simplicity, therefore, presses programmers not to build simple systems - for such systems must admit no composition - but to build systems composed of as many simple parts as possible. Although, "Simple," stands as a black-and-white concept, this number-of-simple-components view allows us to grade on a scale. Figure 3 presents the system of figure 2 but refactored to maximize the number of structurally simple methods.

Figure 3: A structural improvement - image by Spoiklin Soice

Figure 3: A structurally superior system.

In figure 3, b()'s dependencies have been shunted up to a(), increasing the number of structurally simple methods though at the expense of making a() "more" composed. (Pragmatically, this increase of a()'s responsibilities might be offset by ensuring that a() concentrate on its composition and nothing else. That is, a() should coordinate the calling of the other functions and have no other significant functionality.) Nevertheless, figure 3's system is, "Structurally simpler," than that of figure 2.

Note the shape that has emerged in figure 3: this is the classic sunburst pattern, derived from attempts to limit ripple effects and design code that is inexpensive to change because changes to one part of the code to not affect other parts.

Is it not satisfying that the drive to eliminate deep code arrives at the same structure as does the drive to maximize simplicity?

Summary

When an angry programmer roars as you, "Keep it simple, stupid!" he is not inviting you to invent a solution that is easy to understand but to invent a solution the majority of whose components depend on no others with those few components whose task it is to compose those other components being left largely to coordination roles.

"K.I.S.S." has a better ring to it, though.