Incomplete essay on software complexity. An attempt to distil my thoughts into some semblance of order. Last edit: 27/05/2020

Lehman’s law states the following:

An evolving system increases its complexity unless work is done to reduce it.

A system consists of many elements that interact with each other. Those elements are usually subsystems. Sometimes they are independent, sometimes they are interconnected. They are viewed as a whole from a macro-level view.

A software product consists of many features. Features interact with each other as a coherent whole to achieve the main objective of the product. Sometimes they are independent, sometimes they are interconnected. What seems independent from a top-down view (what the end-user sees), is overwhelmingly connected from the bottom-up view (what the developer sees, on the low level of implementation). And sometimes it’s vice versa.

A software product, like most systems, is complex and dynamic. Features are added or removed according to new requirements coming from the extrinsic environment. This occurs frequently and irregularly. There’s no way to stop it as long as the product serves the users in the domain.

Requirements don’t only arise from the extrinsic environment. Working on the product by itself generates more requirements accordingly. These requirements may not have been anticipated when the product was initially designed.

On the code level, any realistic implementation of the code you write depends on and is coupled with another code. Any change in your code must consider all the other dependencies.

A software product is a nonlinear function. Different changes or additions lead to unpredicted complexity.

A typical product starts small, but somehow in a blink of an eye, it becomes complicated.

Adding new features blindly without reasoning about the complexity it adds over the existed product, on its lowest level of code implementation up to the highest level of the end-user experience, could be the certain death of the product.

When a feature is added, it’s hardly removed. There’s no undo. The feature may have other features depend on it. Or even, the written code might be coupled with other code. Or worst, the database structure would be affected.

Mathematically speaking, adding a single feature to the product doesn’t increase the complexity from n to n + 1. For interconnected systems, the complexity goes from n to (n+1)!

Reducing Complexity

On Product Level – Just stop adding new features, or at least add them mindfully. It would simplify the ongoing development of the product and enhance the end-user experience. All the effort that’s going to be invested in new features, will be redirected to enhance existing ones.

Position your product, and have the power to say “no” to new features. Otherwise, who knows where your product will end up with.

Stick to your minimum features, most probably they are enough.

On Code Level – If you are a developer and forced to implement new features, try to reduce the complexity of the code instead. Sometimes complexity is unavoidable. It emerges from core functional requirements. But a huge part of the complexity emerges from poor design, or by bad code practices.

Martin Fowler wrote about a metaphor called The Two Hats, mentioned in his book Refactoring:

When I use refactoring to develop software, I divide my time between two distinct activities: adding functionality and refactoring. When I add functionality, I shouldn’t be changing existing code; I’m just adding new capabilities … When I refactor, I make a point of not adding functionality; I only restructure the code.

Adding new functionality increases code complexity. Refactoring reduces its complexity.

Switching between two mindsets while the system evolves is important to keep it under control. Software can easily trick you. You may find yourself writing several lines of code here and there, and suddenly it’s out of your control. Every line of code you write is a debt you carry over the entire project lifecycle. Write code mindfully, and avoid writing code as much as possible.

Reducing complexity requires you to pause and reflect back on everything you have done, and everything you do. Pay attention to the complexity before it arises, and always make the effort to reduce it.

Just like what the Second Law of Thermodynamics states and all the natural systems tendency: everything goes toward complexity, and effort is always required to reduce it.

Additional Resources