Us developers are fighting complexity all day long. Without pouring energy into keeping a system entropy low, any system will tend to get more disorganized and complex.
Low complexity is necessary to improve, maintain and enhance any system, hence we are trapped in an uncomfortable trade-off.
From one side we want to improve a system, add features, fix bugs, add integration. From the other side we wish to work with systems that are not complex yet and where is easy to make changes.
Embrance complexity
As often is the case, instead of fight an enemy, we are better off understanding it deeply and exploit it when is necessary or convenient.
In the successful project that I happen to follow, there is always a common pattern that I find useful to apply.
Complexity is at the same time vigorously fought and embraced with patience.
In those projects there are parts of the codebase where we keep think about re-structuring and re-factoring our code, we always try to keep the interfaces as small as possible and even trying to make then smaller. In those part we are almost afraid of if
and conditions. Moreover everything is well documented and explained. We even explain what was tried and why it didn’t work.
While on other part of the project we just don’t care about complexity, when a problem arise usually the solution is to just add another if
to check that yet another condition is checked.
Complexity as lubricant
The tricky part is to understand where complexity should be fought and where it should be embraced.
The more fundamental and algorithm heavy is the code, the less complexity we try to introduce. Indeed there is already the “inherent complexity” of the problem. This means force precise boundaries in the system.
We need to know extremely well the shape of our input, as well we need to understand very precisely what side effect our software will bring to the whole system.
On the other side, the more boring and tedious is the code, the more we can get away with adding some “incidental complexity”. One more if
to check another condition is not going to make the code impossible to follow, maybe more tedious but not impossible.
These parts of the systems are usually the ones that
Indeed it helps me to consider complexity as a lubricant, motor oil, that is cheap, ugly, nasty and necessary to make the well engineered motor works without hiccups.
A reasonable compromise
If we could build a completely well engineered system, that would be ideal, unfortunately is too expensive for the majority of the cases. Hence we need to find a compromise.
The quicker the complexity of a part of the system increase given more features, the better is to have such part well engineered.
If adding a new features or fixing an incorrect behaviour on a nasty corner case means adding several checks to several condition in very different parts of the project, it means that the complexity is not scaling linearly in that part of the codebase, and probably, that part, should be re-engineered.
If handling another corner case, means to simply add if