The Heat Death of a Software System
One of the main things a software developer does is imposing order on the unstructured, and invoking it from the non-existent. The natural enemies of this endeavour are entropy and (often enough, if we’re being honest) reality.
Now, we may not be able to do anything about reality breaking our stuff - but entropy is a different story.
When we remember something, it’s not a simple read-only operation, but a process of interpretation and reconstruction. It’s lossy. Even worse, the result of this mental work tends to overwrite what was there before, making the concept of a memory more similar to a suspended thought, than to a record from an archive. It’s constructive in one direction, with a subtle destructive noise surrounding it.
In a way, developing software is similar: You go in and mean to just change one thing, fix a bug, add a feature or restructure a section of code to simplify something. It’s purely an improvement - or so it would seem. But like in the memory metaphor, there are unintended side effects. Reading code, creating a mental model of what it does and the concepts at play, is a process of interpretation and reconstruction. It’s lossy. Even more so is the process of extending those concepts and transcribing them in code again. And for better or worse, the result of your work will overwrite what has existed before.
A side effect of working on software is introducing software entropy. It’s a slight, constant, multi-level drift of all concepts and components into random directions. While it is possible to limit the amount of added entropy, it can’t be avoided entirely and easily goes unnoticed:
- Writing source code can cause a drift in code style.
- Tweaks in functionality can cause a drift in purpose.
- Generalizing a component can cause a drift in its intended use case.
- Adjusting an algorithm can cause a drift in edge case behaviour.
- Changes in appearance can cause a drift in the user’s mental model.
- Re-wording comments or names can cause a drift in modeled concepts.
All of the above can be very subtle. They may not cause issues at all and can build up over a long time. It’s only when the accumulated drift crosses a threshold that it reveals itself as a regression.
The good thing is that regressions can be fixed, and it happens all the time, so we’re not exactly powerless here. But who wants to fix regressions? If we could avoid them in the first place, that would be even better.
There are a couple of things that I found helpful in reducing that kind of entropy, and I’m suspecting that style consistency and documentation quality directly influence how much accidental drift a project is exposed to.
Maintaining a consistent style across the codebase helps a lot in reducing entropy on the lowest level: Once it has been internalized, the mental overhead of understanding and writing in different styles is removed, no longer distracting from the task at hand. It’s one moving part less to worry about.
It can also serve as a subtle hint towards the reader: This is a tidy place. Please tread carefully. It may not be great science, but I’m pretty sure most developers are more inclined to do their best when working in a pristine textbook-quality file than they would in a sketchy place where the parenthesis are all wrong and half the equality checks are missing a space.
Documentation is of course incredibly important. Code is a lossy compression mechanism for concepts and strategies, and it’s up to the developer to interpret and rephrase it every time they make a change, which is naturally prone to drift. Every comment and piece of API docs can help to make this process more lossless. Seeing it this way, documentation is not some sort of semantic sugar sprinkled across the codebase, but a vital (and second-degree functional) part of the code itself.
Even with guidelines and good intentions, documentation rarely turns out to be as complete as everyone would want it to be though - and this is where having some sense of code ownership can help. Source code having an owner doesn’t mean nobody else is allowed to touch it, and ownership rarely remains exclusive as scope increases. Instead, it’s about (a) who is a first responder for required changes and (b) who to ask in case any questions arise. Both can act as a safeguard against accidental drift and gradual regressions.
And of course there’s automated testing, which is great to lock existing behavior in place, and avoid introducing bugs or unexpected results. It reduces drift on a functional level, similar to how documentation reduces drift on a semantic level.
Finally, I think it makes sense to broaden the term regression itself, and to be vigilant of a variety of different ways a software can lose value, as perceived from a variety of perspectives. A regression could be any of the following:
- Functionality is broken and no longer usable
- Performance is worse than before
- Increased complexity of code or UI
- Maintenance effort for a component is increased
- The clarity of a concept (code or UI) is reduced
- A user has more trouble or needs more time to achieve X than before
- Code is harder to read or understand without apparent reason
- The number of external dependencies is increased
- A breach in established style, code patterns or concepts of the project that might throw off future or existing contributors
…and probably a lot more!
The good kind of noise
I’ve been bashing random drift a lot in the previous two sections, but it’s not all bad. Unlike I suggested, it’s not truly random either: Every change introduces its specific drift for a reason, and often one that can be easily explained when you ask its author. It’s only when zooming out far enough that the sum of these changes constitute a noise that can appear as random, entropic drift.
Sometimes the reason for drift is misunderstanding or oversight, but sometimes it is conscious refinement or optimization. Not all drift is bad, as some of that noise ends up fueling the search for a better local energy minimum. Who cares if the semantics of a class change slightly when its new form happens to fit its use case better than before? Entropy doesn’t equal regression - and even some of the actual regressions are necessary for an improvement in the bigger picture.
I guess my point is to be aware of the subtle changes. To keep an eye on drift, take measures to reduce its impact, and handle the rest with a healthy amount of vigilance and communication. Entropy happens where mindsets don’t quite overlap, and it can turn out to be both a challenge and a chance.