The reality in software development is that very few of us ever get to work on a greenfield project. Even our own personal projects start as shiny oases of pristineness. But even they eventually turn into Franksteins that we are afraid to open again.
Most code, OK maybe all code, becomes legacy code.
What do we mean when we say legacy code? It's generally not viewed as a gift that someone left you. In fact, it's often the inverse. Legacy within the confines of code is generally a piece of code that is difficult to work with. This could mean it's riddled with bugs. It could mean it's hard to reason about. Or it could mean that it's unmaintainable. Some of the time, it's all these 😬.
In this post I want us to take a step back from the stereotypes surrounding legacy code. Instead of focusing on all the bad associated with it, let's look at three tips that can help you see the bright side of it. Or at the very least, understand it.
Tip #1: Understanding before judgment
Don't judge a book by its cover
One of the most famous idioms in the English language. It suggests that one shouldn't prejudge something or someone merely on its outward appearance. The same is true for legacy code.
A piece of code was written by an individual that was given a set of requirements to fulfill. Sometimes that individual is still at the company and other times they have parted ways. No matter which, it is dangerous to judge the legacy code before understanding its purpose. Also, it is unproductive to judge the individual who wrote it.
Instead, approach a piece of legacy code as an opportunity to learn. A chance to learn about the prior decisions that existed before today. What problem was it trying to solve? What were the business considerations when it got introduced? What was the timeline?
All these are questions that are worth asking when reading any piece of older code, good, bad, or otherwise. The reality is that code solves business needs and those needs evolve over time. So what you are looking at from years ago might not match the needs of today.
Tip #2: Context is King
Cornel West has a famous quote:
Context shapes who you are
The same is true for pieces of code that are years or even decades old that are lying around in codebases.
Like the earlier point about having understanding before pre-judging something. But the idea of context expands on this idea.
Codebases are hardly ever a few hundred lines of code. Usually, they are more like hundreds of thousands of lines of code to support a given application. We could have a sector of code for account billing. Another for data processing, etc, etc.
Each sector or area of the codebase has its own context. Each has its own set of circumstances, requirements, and decisions that are set at a point in time. Changes to a piece of code without understanding the what and why behind each of these is dangerous.
Without context, you could think you're improving a piece of code by refactoring it. But in reality, you could be breaking the entire billing system.
This doesn't mean don't change existing code.
It means understand what the existing code is doing and change it to improve something. Don't change it blind and without a valid reason.
Tip #3: If it's not broken, maybe it doesn't need to be fixed
Developers, each and every one of us, have opinions about code.
Some of us prefer spaces and others prefer tabs. Some of us like single-letter variable names and some of us want specific names.
When coming into a legacy codebase we are going to have a set of pre-existing opinions. We are often going to want to start changing a bunch of things to match the way we work. This is OK, but in moderation and with a lot of precaution.
The reality is that code is written to solve a problem and provide value to the user who is using it. If it works to solve the problem without any glaring issues, then it doesn't matter what our opinion is of it. If a piece of legacy code isn't broken and it's solving the problem it was meant to solve, it doesn't need fixing.
That said, as we have already discussed, businesses and problems evolve. The code that solves problem X may not solve problem Y. Or maybe it does but problem X expected 100 requests while problem Y expects 1,000,000 requests. In these cases, the solution isn't broken it just doesn't scale. If we find ourselves in this scenario we should make a concerted effort to extend the solution or lean into the two earlier tips.
Legacy code, tech debt, and refactoring are all inevitable in any codebase. The reality is that very few developers ever get the opportunity to work on non-legacy code. In fact, you may get to work on a new codebase that then becomes legacy code, and you wrote it!
When it comes to the process of working with legacy systems, practice understanding. Understand the situational context it was created in. We don't know or don't always remember the context and business requirements a piece of code was created in. So be careful not to judge it or the person behind it. Treat it as an opportunity to gain more knowledge and better the system.
Be mindful of changing legacy systems that are functioning and serving their purpose. They may not be the way we would have done it, but if they are working, maybe it's not so broken.
Most importantly, don't fear legacy codebases. They offer unique challenges and opportunities to learn things we may not have learned otherwise.