Striving for simplicity
There is no way to run software in full isolation in today’s world. Regardless of how the software is built and deployed, it will always depend on other things. It will depend on the environment it runs in, it will depend on other software that runs in that environment, it will depend on resources like network, CPUs, Memory, etc. This interdependence is not new, it is expected,, and still fascinating.
The challenge of striving for simplicity is particularly difficult as it requires a balance between understanding the user’s needs so that they can be simplified and providing the user with enough power so that they can achieve their goals.
One common failure of complex systems comes from software that makes certain assumptions about the environment it’s running in and about the other software it has to interact with. These assumptions can vary in type and I don’t think this post will be able to cover them all. But, before we get into the details, here’s a short list of things I have learned from having dealt with them in the past.
Take aways:⌗
- Avoid making assumptions about the environment
- Especially, don’t make assumptions about parts of the environment you don’t own
- Provide as much flexibility as possible as long as the default behavior is sane for the majority of use-cases
- The goal is not always to hide the complexity but rather make it easier to navigate it.
Making assumption about resources that are available⌗
This happens when software makes assumptions about what has been made available to it. It may assume enough storage (if at all) has been made available. It may assume that there’s enough memory for it to run. It may assume that certain tools are available, or that it is running under some specific OS.
Some of the points above may sound naive and they could be covered by compiling a list of pre-requisits that are needed for the software to run. But even in the presence of such pre-requisit, every assumption poses a risk to the whole environment.
Making assumptions about the resources that can be altered⌗
This happens when software makes assumptions about what it can do to the resources it has to interact with. This one is a bit hard to explain without an example so I will share one that served a bit as an inspiration to this post. I will leave the names of the tools out because my goal is not to rant about any specific tool and I believe bringing the tools into this post would distract us from the actual point.
Let’s think of a tool whose responsibility is to reconfigure the network of a specific host in preparation for other software to be able to work properly. The tool gathers some information about the network in the host. The host has 2 networks configured (A and B). Network A is the one that is used as the default route of the cluster while network B is the one that we would like this tool to pick and reconfigure. This tool, however, decides what network to use based on the default routes of the host and, therefore, because A is the main network, there is no way for this tool to pick the right interface. Finally, and to make it a bit more complex (and worse), let’s assume that we found a way to tell this tool what interface to use. The tool now goes off and changes the default route of the host, which ends up breaking the connectivity to this host entirely.
The tool is not entirely at fault here, it needs to reconfigure the host’s network so that this other software can work. The problem here is the assumptions the tool is making about the rest of the environment and the underlaying infrastructure. Some questions worth asking are:
- Is it correct to assume that there will always be one network connected to the host?
- Is it correct to assume that the network picked for this tool should be the default network of the host?
In this scenario, the assumptions made by the tool put at risk the host where it runs and the rest of the infrastructure thatdepends on the software this tool is preparing the host for.
Assumptions masked as simplicity⌗
We often make wrong assumptions while striving for simplicity. It’s an trap I have fallen into many times. Truth is, I have fallen into this trap so many times that I have started noticing the same pattern elsewhere and more often.
The trap is that we often make decisions on behalf of our users and in the name of simplicity instead of making it easier for users to decide.
Simplicity is the act of abstracting complexity. Therefore, something is simple iff the required knowledge for understanding it is the minimum needed. 2021-12-07
Striving for simplicity does not always mean abstracting complexity. This is an important distinction to keep in mind as it may often lead us off track. To abstract complexity requires us to understand what the environment looks like, what context users need to have in order to use the tool, what needs to be hidden from them, and what is worth exposing. Abstracting complexity often requires us to bring linearity to something that may not be.
Sometimes, however, complexity should not be abstracted but rather made easier to navigate. What this means is that we can expose all the complexity of a system while still taking care of the generic, more simple, cases.
This distinction between abstracting complexity and making it simpler is important as it influences how we address the problem at hand. In one case decisions will be made to hide aspects that are believed to not be relevant to users, whereas in the other case all these aspects could be exposed. The relation between what is exposed and what is abstraced varies depending on whether we are trying to abstract complexity or not.
It goes without saying that everything comes at a cost and there are trade-offss in every choice. We may be quick with off loading this issue to other teams (like product) but that would be a mistake. Other teams can only go so deep, the implementation does not belong to them.