3 Patterns that keep System Complexity under control

Knowing what system complexity is is good. You might ask though, how you can identify system complexity and how you can manage it. In this article I will introduce the three manifestations of system complexity and three patterns to control it.

3 Patterns that keep System Complexity under control
Photo by Steve Johnson / Unsplash

I learned a lot about the term "System Complexity". Different definitions exist and smart people divided the term into complexity that affects the code (program complexity) and complexity that touches the human aspect of comprehension (psychological complexity).

Now, that's all great but how does complexity manifest itself you might ask. I will explain three manifestations of complexity in systems[1] and three patterns to keep complexity under control.

The 3 manifestations of complexity

There are different characteristics of system complexity. They show in different ways. However, they often fall within one of the following categories.

Amplification of change

Have you ever sat at a task and thought "This is a simple change, I'll get this done today" - and then things developed into a completely different direction? Instead of just adding this one data field and adapting tests, you found a rabbit whole in front of you. Instead of a change in one place, this change amplified and made you wade through many other parts of the code base.

This is what amplification of change means. An apparently simple adaption requires code changes in many places.

Amount of cognitive load

Sometimes the simplest change can create a knot in your head. You just cannot understand how things tie together. Reading tests - so they exist - is not helping either. This stuff is not very descriptive. You probably need to ask other people to explain how this works. But even they still have a knot in their head from the last time they looked at this.

Cognitive load is the extent of knowledge a developer needs to have in order to finish a task. More lines of code does not necessarily equal higher complexity. More lines of code could as well help to clarify the functionality and reduce the hidden magic that is happening.

Amount of unknown unknowns

Unknown unknowns make a system unpredictable. You cannot know what you don't know. A new feature can make a major refactoring necessary, and the situation will only become clearer the more time you spend. A system might consist of several parts that communicate asynchronously with each other. Having all imaginable scenarios laid out at the beginning is often not possible. The longer the system runs, the more you will learn which scenarios you have not yet thought about.

Unknown unknowns make it difficult to understand which parts of the system need to be adapted or what information is necessary to do that.

Now we know what complexity is and how it can show when we work on a software system. Let's try to go into more details of how to mitigate those kind of situations.

3 patterns to keep complexity under control

According to Sturtevant[2] there are three widely used patterns that can keep complexity under control. Those are hierarchy, modularization as well as layers and platforms.


You can think of a hierarchy as a tree, layers or boxes-within-boxes. The idea is that components in the system perform sub-functions that contribute to the overall function.

How to achieve that? You discover ways to decompose parts of the system into semi-independent components. Then, those components can be worked on somewhat independently as long as the interfaces are clear. This creates benefits for the people working on the system because it reduces the cognitive load to merely understand the component at hand and its interfaces.



A module is a semi-autonomous structure with defined boundaries. Robust modules have two characteristics. First, their internal functioning is hardly influenced by outside effects. Second, they can evolve independently without influencing other modules. That's the world every developer dreams of!

One key aspect of modules is that they hide information. Back in my teenage years I built and repaired computers myself. If you ever did that yourself, you built a system that is comprised by many modules. You have a CPU, GPU, RAM, mainboard, storage and more. All of those communicate with each other via defined interfaces. They would be nothing on their own, but only function as a whole. And each of those parts hides lots of information that the other parts don't need to know about. Because of that modules are flexible, can be split, substituted, augmented, excluded, inverted, and ported. We will look at the aspect of information hiding in more detail in another post.

However, nothing is for free. Modules have restrictions. Not every system can simply be modularized to the extend one would like to have. Modularization has costs as well, in terms of defining the interactions of modules and managing them. There might be performance limitations as well. For example, heat dissipation and form factor push computer designs towards more integrated systems - nowadays especially in laptops.


Layers and Platforms

Layers and platforms combine the notion of hierarchy and modularity. They can be seen and used independent of each other. A layer provides functionality to components above and relies on services provided on lower layers. Abstraction layers can combine independent parts into a useful whole.



We have taken a look at three manifestations of system complexity and three high-level patterns that can help to keep system complexity under control. If you have ever worked for a considerable amount of time on a software project, you have probably seen them all. Using the patterns is by far easy - especially over time and with changing people working on the system.

That's why, in a following post I will go into more details about how to improve information hiding in modules.

  1. J. K. Ousterhout, "A philosophy of software design", Vol. 98. Palo Alto, CA, USA: Yaknyam Press, 2018. ↩︎

  2. D. J. Sturtevant, "System design and the cost of architectural complexity", PhD diss., Massachusetts Institute of Technology, 2013. ↩︎