Zach looks at building software as piling up grains of sand one at a time—and then managing the landslides when the sand piles get too steep.
Imagine that every second you’re dropping a grain of sand onto a table from the same fixed position above. As the sand continues to pile up in a heap, what eventually happens to the pile? It landslides! If you keep adding a grain of sand at a time to the pile, it will continue to pile up into a heap, and then at some critical point another landslide or series of landslides will occur. For as long as you add sand to the pile, the pile continues to grow, landslides occur, and the pattern repeats.
Now, imagine that each grain of sand represents functionality in a software system. As you add more functionality, the system piles up into a heap, and at some critical point in the system a landslide occurs. If you continue to add functionality, the system grows, landslides occur, and the pattern repeats. In this analogy each landslide in software represents a state where the system (either in whole or in part) has attempted to go just beyond its maximum threshold for maintaining structural integrity and sustaining growth. The system is in a chaotic state and the effect is felt throughout.
What’s interesting about software is that functionality is never consistently added to the same spot. This changes the sand pile metaphor slightly; no longer do grains of sand drop from the same fixed position, but instead the position moves in accordance with the area of the system we’re working on. As a result, a single pile no longer builds into a single heap from the start; rather, several smaller piles grow into several heaps over time.
We’re developing a metaphor here, but it’s starting to get complicated. Let’s take a step back and look at what a concrete yet simplified example of this sand pile metaphor might look like in software.
Take a monolithic program that is written in a single file, in which we only have global variables and functions available (no classes, no modules). To illustrate the sand pile metaphor, each piece of functionality we add is akin to dropping grains of sand from a single fixed position. The more functionality we add, the more code we write, the larger our file gets, the more polluted our global name-space gets, the more complicated our program becomes, and the bigger our sand pile gets. Subsequently, as the program grows it becomes more difficult to maneuver about, and adding more functionality becomes more arduous with every addition. This difficulty over time can be expressed as a property of our program, at least in how we’re growing our system. We’ll refer to this difficulty at a point in time as the system’s “critical value.”
Eventually, the program’s critical value reaches a point where functionality cannot continue to be added until something is changed. When the program reaches this point it has grown unwieldy and cumbersome to work with. This point can also be expressed as a property of our program: the “critical state” of the system. The critical state represents the highest point a sand pile can grow to before a landslide must occur. At this point, our ability to continue to grow the program has come to a halt.
In the natural world, when a landslide occurs, it shifts the critical value of the sand pile back down, away from the critical state, but it never actually resets to 0. However, in software, landslides aren’t naturally occurring. We, the developers, have to cause them through refactoring, reorganizing, and simplifying, among other things. This helps bring the critical value down, away from its critical state to where the system can continue to evolve. With the program in an evolvable state, we can get back to adding functionality.
There’s actually an inverse relationship at work here: as the critical value increases toward the critical state, the program’s ability to grow declines.
Back in the 1980’s, Meir Manny Lehman observed this relationship in practice: “As an evolving program is continually changed, its complexity, reflecting deteriorating structure, increases unless work is done to maintain or reduce it.” Lehman’s observation goes one step beyond where we’ve gone, but we’ll get to that shortly.
Most programs today are not nearly as simple or limited as the described monolithic program. Even the simplest mobile and web applications are more complex because of the rich environments, languages, and frameworks available to them. But that’s okay, because understanding how the monolithic example works gives us insight into how this metaphor applies in more complex systems (smaller components being used together to create a larger system); which really means we’re dealing with more sand piles, but in the same sandbox.
Before we dive into that, there’s another facet of this sand pile metaphor that we should take a second to consider: stability.
We already saw how as the sand pile constantly grows it will eventually landslide. It’s not a matter of if, but when. We’re going to make a few more observations about how we can apply the sand pile pile as a metaphor.
First, a smaller/shorter sand pile represents a less complex system or component in the system.
Second, the larger/taller the sand pile is, the more complex the system or component in the system is.
Third, the flatter the pile, the more stable the system is.
Lastly, the steeper the pile the less stable the system is. And by stable, we are referring to a system that is able to sustainably evolve with new or changed functionality; we’re not talking about bugs in the system (which is an interesting correlation, but outside the scope of this article).
This means that a large system that is very well-factored would be represented by a bunch of sand spread out with no jagged mountains or peaks. On the flip side, a smaller system that is very complex would have several sand piles clumped together looking like a mini-mountain range covering a much smaller surface area.
This isn’t to say that all complexity is bad. You may have complex things in your application that are necessary. However, if the structure and design of your code continues to “deteriorate” as Lehman put it earlier, then you’re going to end up with sharper peaks (you’re getting closer to causing a landslide); whereas complexity that is reduced to its simplest form may be tall but will be round or flat on top of the pile.
Additional complexity and the deteriorating structure of code over time is a naturally occurring part of building software, just as the growth of a sand pile is naturally caused by dropping grains of sand on top of one another. As software is built and grown, all of the observations we’re making with the sand pile metaphor hold true: adding functionality is akin to dropping more grains of the sand. How that sand is distributed is what ultimately determines its size, distribution, slope, and peak. All of these properties are reflected in the software’s overall stability as expressed in its ability to change and evolve and continuously allow growth of the system.
Developers are deeply involved with how the sand piles are formed. We can clean as we go and keep a relatively flat distribution of sand. Sure, there may be some clumps and even some peaks, but if they’re kept to a minimum then our system is likely kept in a stable place for continual growth. Or, we can ignore the impact that comes with each change and hope that it doesn’t catch up to us. If you’ve ever been on, or observed a project which got off the ground at a very rapid pace only to come to a sudden halt, then you’ve likely experienced or seen this.
This sand pile metaphor offers a simple and easily understandable way to think about the complex activity of constructing software, both conceptually and visually. It also exposes a facet of software development that is often overlooked: a change of any size in the system can cause a landslide. A landslide indicates that the system is so bad that the team has to stop moving forward in order to rework the system. This can take a significant amount of time and one landslide may likely cause several landslides to occur following it.
In my experience, when teams do this they usually don’t take this clean-up far enough. Instead, they do just enough to keep moving forward again, which sounds very pragmatic, but in reality, they’re taking a highly unstable system, barely making it stable again, only to shortly thereafter be in the same unstable situation. Rather than continuously moving at a swifter pace the team is always moving at its slowest possible pace because of the poor state of the software. This vicious cycle is often detrimental to the entire effort of building the software in the first place.
In his book Deep Simplicity, John Gribbin notes a similarity between the sand pile model and earthquakes saying that “just as in the way real earthquakes of any size can be triggered by stimuli of any size, adding a single grain of sand may cause one large avalanche, or a series of small avalanches, or just leave the new grain of sand delicately balanced on the pile.”
Admitting that any change, no matter how small or big, could cause our system to reach its critical state may be tough to swallow, but that doesn’t make it any less true. Understanding this may afford us the humility to accept that we’re better served by curating and caring for our systems as we grow them, much like a gardener with their garden. This isn’t about seeking perfection in our systems, but rather keeping our systems in a constant state of stability that lets us continuously and consistently evolve the software we build in order to provide value to its users and its stakeholders for its lifetime.
How’s the landscape of your software?
For more information on the origins of the sand pile model we’re using as a metaphor please refer to the work done by the physicists Per Bak, Chao Tang, and Kurt Wiesenfeld as they explored the behavior of complex systems on the edge of chaos. (I spoke on the same topic recently here.)
Zach Dennis is a co-author of The RSpec Book and a co-founder and fellow human at Mutually Human Software, an expert custom software strategy and design consultancy in Grand Rapids, Michigan. He has been enjoying Ruby for nearly a decade and has contributed to several projects such as Ruby’s standard library documentation, Ruby on Rails, and RSpec. In his spare time, Zach loves spending time with his family, continuously learning, playing music, and running continuousthinking.com.