|« Test Driven Development||Controlled Access of Global Data »|
What is code rot
A.K.A software rot, code decay, code entropy; there are many similar names.
Code rot: A term used to describe the quality of source code as it changes over time and migrates further away from the orignal design. This may continue until the code is no longer viable. A passive use of the term code rot describes the source code for an aging system that require dependencies or tools that are no longer available. Eventually the hardware fails and there is no way to update or port the software to newer tools.
This entry will focus on the former variant rather than the latter because it describes active code rot. Each and every change to software can introduce decay. Please recognize that rot, decay, and entropy, these are all just another word for risk, which is the potential for a problem to occur.
Divide and Conquer
The way we build computer programs is with Divide and Conquer. Take the problem at hand, and break it down into a set of simpler problems, and work towards solving the simpler problems. Continue this process until you reach a level you can instruct the computer with your implementation language of choice. Take what is complex, and rework it until it is simple. Essentially we build a codebase one line at a time.
Every change to a program should be intentional and add value. Because every change has the potential to add the risk of new defects in the program. Unmanaged risk in development will eventually be realized as bugs. Consider yourself lucky if the bug presents itself immediately. These potential problems will linger in the code and collect silently over time. Until one day, when changes are added that start to expose these lingering problems. If this process were given a name, I would choose something that meant the opposite of Divide and Conquer, possibly Multiply and Fail. Essentially every change becomes a new factor to multiply into the myriad of ways the code will eventually fail.
Bugs aren’t the only potential problem that can be introduced. The quality of the code and design can be compromised as well. This is actually what most developers are referring to when they say code rot. This is the point where adding the most minor change cannot be made without causing one of these hidden bugs to appear, or a relatively significant amount of code is required to implement a minor feature.
It does stink to work in rotten code. Rotten code can turn a new and exciting assignment into a nightmare. It’s difficult to tell how rotten code is unless you actually jump in and navigate through the morass of structured gibberish. Unless you are in the code, you also do not know what qualities and hidden gems remain from the original design and implementation. Therefore, it is very important to trust and guide the engineers that are the most familiar with code to help you make good decisions regarding change.
Change can range from the addition of a minor feature, to a major refactor, or an entire rewrite and integration of a module. Change for change sake is never a good thing when programming. Each of these augmentations requires careful consideration for how and why the new implementation will be executed.
“Those who cannot remember the past are condemned to repeat it”
The continual change of a program is the driving force behind code rot. The amount of impatience, inattentiveness, urgency of the schedule/budget, and in many cases, plain ignorance, all contribute to how quickly code will deteriorate. One line at a time, each feature is built. One object at a time, swaths of code are rewritten to combat the filth that seeps into the program. The only problem is, too many developers are quick to declare “This code is hopeless,” and decide to replace it with something…else.
Learning to understand an existing implementation of someone else’s idea is not nearly as fun or glamorous as designing and developing your own idea. However, there is generally always something to learn from existing code. The lesson may just as often be an example of how not to implement something. Previous mistakes are not bad, as long as you only let them happen once. It’s even better if you can develop enough wisdom to recognize a mistake that you have not made before, correct it, and avoid making that same mistake in the future.
Boundaries are an under-appreciated design principle in software. There are many types of boundaries from the obvious, interfaces, objects, modules, and function calls to the not-so-obvious concept of control scope. Consider the potential value a boundary can provide. Have you used boundaries in all of the contexts listed below?
- Simplify implementations by enforcing the divide phase of our divide and conquer implementation process.
- Become a barrier to prevent rotten implementations from spreading
- Conversely, use the barrier to isolate code so it can be repaired
- Reduce the coupling that tends to causes code to become unnecessarily rigid and complex:
- Hide implementation details to minimize assumptions that cause side-effects
- Prevent direct coupling to the implementation
- Protect the integrity of the design by protecting the integrity of the data
- Use scope boundaries to:
- Limit access
- Perform automated cleanup
- Provide security or robustness with trust
- Protect Intellectual Property
Can you think of any uses that I did not list?
Boundaries are not the only solution to prevent and fix code rot. However, they are the simplest solution that I am aware of that are so versatile and can be used in many different contexts. Next time you consider violating a boundary so you can easily access a some data, think of code rot. It is the collection of these quick fixes, and the unintended side-effect that they introduce that allows rotting to fester and thrive. Hopefully it will help keep your code clean, tidy and a joy to work in.