September 15, 2022
A programming saying to live by is “code that you don’t touch gets rusty over time”. We have to realize that our piece of software is not a completely isolated, independent unit (even when using containerization like Docker) but something that lives in a continuously evolving ecosystem. The platform where we start running the first version of our system is probably not the same one that runs it today. Or tomorrow.
To make things more complicated we rarely build everything from scratch, instead, we rely on dependencies - hats off to the underappreciated community of Open Software - which rely on dependencies, which rely on dependencies… which end up with a huge matrix with constraints on specific versions that we can or cannot use under the hood.
If we neglect the need for regular maintenance we can wake up one day being forced to make a huge change to our system - because we are not able to add a new feature or bug fix or - in the worst-case scenario - we are even no longer able to run the application.
Table of Contents
But first things first - what’s old Ruby code? There is an old maxim saying “the best code is no code at all”. Why? Because every line of code you write is already a legacy code that might contain bugs and needs to be maintained. There is always a risk that a line that works today will stop working tomorrow for dozens of reasons.
That’s why we write automated tests and run them as often as possible so we can find:
There is a huge business risk running on an outdated legacy system - it might slow down the development of new features, make it harder to onboard new team members, trigger a breakdown or be a foundation of security leaks that can cost businesses a fortune.
According to the Data Breach Report by IBM, which surveyed 550 companies from 17 countries in 2022:
Source: Data Breach Report by IBM
The easiest approach seems to be the best one - just make regular updates and test your codebase in the environment as closest to production as possible.
Regular updates are like brushing your teeth twice a day and visiting your dentists every half a year - it does not bring much pain and can help you enjoy a beautiful smile to old age.
It doesn’t guarantee you won’t have any problems but it reduces the risk to a bare minimum. But if you visit a dentist only when pain comes out, you can quickly lose all your teeth.
We’re happy to live in an era when most of the job can be done for us by others. Tools like dependabot or renovate can automatically check what can be updated and prepare pull requests with changes, so it’s mostly a matter of goodwill.
In case you are learning your lesson and you already have an outdated legacy codebase, you don't have to start crying.
But beware of jumping into deep water and start improving everything in a single step. It's a straight path to a broken application with infinite hidden traps waiting for you.
What I would suggest instead is to make as small a step forward as possible. Update one thing by one version, verify if everything works, and repeat. Do not stop working on new features in the meantime since updating itself might be a long process. If there are backward incompatible changes, adjust your codebase first (and break it down to multiple tasks if needed), verify it works, and then make the update. Learning about refactoring patterns can also help to improve legacy codebase in general.
It might be worth mentioning, that a clean codebase with clean architecture, among many other benefits, can also minimize the cost of upgrades or swapping the dependency, or even replacing whole modules of an app with something else, written from scratch or replaced with 3rd party.
The riskiest decision you can make is to postpone security-related updates.
Security updates are easy to ignore with the false belief “it won’t happen to me”, but it’s a real Russian roulette that can kill the business.
The next in line are updates that fix annoying bugs or block your development.
The last one is just regular updates, which are like “brushing the teeth” - it’s not a problem if you skip it from time to time, but don’t make it a bad habit.
In last words, I would like to emphasize the need to perform updates on a regular basis to maintain a healthy application. Underestimating this can hit our business badly in many unexpected ways. So, as always, prevention is better than a cure.
If you neglect that part of development, don’t panic, but simply introduce a repair plan with slow but continuous improvement and sooner or later you can drag yourself out of trouble.