Complexity: What is Complexity
A major principle in software development is KISS (Keep It Simple Stupid), but I have always felt that KISS is something which does not have specified actions and therefore gets ignored in practice. I hope this series of posts will bring you new meaning to KISS, as well as some actionable steps.
Short != Simple
Often when I ask developers to keep code simple, they try to shorten code. ie instead of doing something in 4 lines they do it 1 line.
Often shorter syntax is blurs the intent and makes it harder to read and understand.
Sure it seems simpler when you write it, but it may not be for others or even for you when you look back a month later skimming the code or have forgotten how that syntactic sugar works.
I define simple as the lack of complexity, so let's understand complexity.
Road to complexity
When we first build an app it's simple and everybody understands it. Then each week developers add a few extra lines of simple code, and eventually an incredibly complex system emerges. Developers often don’t notice the growing complexity due to their familiarity with the code. Once the developers start to call the system "complex", they usually don’t know what exactly made it complex or even at which point it became complex.
What if we could keep the complexity down? or what if we could isolate the complexity? or keep modules simple but move complexity to the overall system? In order to answer these questions we need to first ask ourselves, "What is complexity?".
The core cause of complexity is having too much to keep track of. This causes complexity because a developer can only consider a certain number of factors at a time. If there is too much to keep track of we forget some of them, which means more bugs, poor performance or additional complexity.
"Every additional concept that has to be held in mind in order to understand an object contributes to mental overload."
-Domain Driven Design By Eric Evans
Some major causes of complexity are:
- Over Engineering
- Lack of Readability and Discoverability
- Poor architecture - Irreversible decisions
There have been a few suggestetions on how to measure complexity:
- lines of code
- cyclomatic complexity
- code duplication
The problem comes in when we can measure some parts of complexity but not others. What we tend to do is base our complexity on what we can measure and forget about the other parts. Because we can measure aspects like lines of code or code duplication we over optimize for them and we use them for everything without pausing to consider the effects on the aspects we can not measure.
We can NOT accuratly measure:
I'm going to break down all the double edged swords in common coding practices to shed some light, on why our applications become big balls of complex mud.
Choose your poison
"I believe the hard part of building software to be the specification, design, and testing of this conceptual construct, not the labor of representing it and testing the fidelity of the representation. We still make syntax errors, to be sure; but they are fuzz compared with the conceptual errors in most systems. If this is true, building software will always be hard. There is inherently no silver bullet."
The aspects which we can not measure, usually come down to a gut feel but this gut feel is often highly conflicted, even amongst highly experienced engineers and archtiects. You may notice some conflict in design advice (even in these posts). This is due to the fact that everything you do probably has some kind of trade off somewhere. For example: Trying to avoid excessive amounts code paths, may land you with exessive generic solutions, and visa versa. Since there is no 100% correct answer which fits every scenario, you need to try balance the trade offs, and decide what you can live with.
Kubernetes and GoLang is an example of how some modern teams which are developing some of the latest greatest technologies, are putting a strong emphasis on simplicity. If you take a look at this Kubernetes Controller on GitHub you will see how they broke every rule in the book. They call this "space shuttle style" because it is “the same way code is written at NASA for applications like the space shuttle”.
// ================================================================== // PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE. // KEEP THE SPACE SHUTTLE FLYING. // ================================================================== // // This controller is intentionally written in a very verbose style. You will // notice: // // 1. Every 'if' statement has a matching 'else' (exception: simple error // checks for a client API call) // 2. Things that may seem obvious are commented explicitly // // We call this style 'space shuttle style'. Space shuttle style is meant to // ensure that every branch and condition is considered and accounted for - // the same way code is written at NASA for applications like the space // shuttle. // // Originally, the work of this controller was split amongst three // controllers. This controller is the result a large effort to simplify the // PV subsystem. During that effort, it became clear that we needed to ensure // that every single condition was handled and accounted for in the code, even // if it resulted in no-op code branches. // // As a result, the controller code may seem overly verbose, commented, and // 'branchy'. However, a large amount of business knowledge and context is // recorded here in order to ensure that future maintainers can correctly // reason through the complexities of the binding behavior. For that reason, // changes to this file should preserve and add to the space shuttle style. // // ================================================================== // PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE. // KEEP THE SPACE SHUTTLE FLYING. // ==================================================================