The Anti-Legacy Manifesto: Writing Code That Lasts

The Anti-Legacy Manifesto: Writing Code That Lasts
Photo by Narciso Arellano / Unsplash

As a junior engineer, I had that classic arrogance many of us start with - spitting at any code I hadn't written myself.

I'd scroll through the codebase, commenting about the "idiots" who'd created this "legacy mess" I now had to maintain. The complaints about fixing someone else's mistakes were constant. I was young, overconfident, and utterly wrong.

This reality check came while working for a client with a relatively new but poorly written codebase. Our team inherited the project, and despite our efforts, we only managed to patch things up around the edges. The client was happy enough with our solutions to extend our contract, but the core issues persisted.

Then, the client decided to do the git provider migration. We had to create a new repo, copy over the code, and push it up. It was an easy task for me, but when I looked at the commit history, I saw only my username staring back at me. Future engineers would git blame me for every line and curse my name for the "legacy nightmare" they inherited, just as I had done to others.

The irony hit hard - I'd become the faceless "idiot" I used to blame.

That day transformed my understanding of legacy code. Sometimes you're not the hero coming to fix things; you're just another engineer in a long chain, doing their best with the constraints they had.

It was an experience where I learned that the word "legacy" is a misused and toxic label we've attached to working software. The real issue isn't the aging code - it's our dismissive attitude toward it.

When we brand code as "legacy," we create a self-fulfilling prophecy that leads to helplessness and expensive rewrites.

We treat unknown code like a defective product that needs replacement when really, we just don't understand the problems it was built to solve.

Understanding Code Aging

When engineers call code "legacy," they usually mean they didn't write it, don't understand it, and feel anxious about modifying it.

Sometimes it points to genuine issues like poor quality or outdated tech, but more often it's a smokescreen for unfamiliarity.

The irony? If the original engineers were still maintaining the system, it probably wouldn't be called legacy.

This reveals an uncomfortable truth: the "legacy" problem often lies not in the code, but in our relationship with code written by others.

Understanding what makes code "legacy" reveals some strategies to prevent it:

  1. Stable Teams: Long-term team members maintain project knowledge. When authors stick around, their code stays understood and maintainable.
  2. Architecture and documentation: Well-structured, clearly documented code survives developer transitions. Good architecture and documentation act as knowledge bridges to future maintainers.
  3. Collaboration: Regular pair programming and thorough code reviews spread knowledge across the team. When multiple engineers understand each component, no single departure creates a knowledge gap.
  4. Junior Engineer Training: Giving ownership of components to junior engineers ensures knowledge transfer and creates a pipeline of maintainers who understand the system.
  5. Tech stack: Using standard, proven technologies makes knowledge transfer easier. Chasing cutting-edge tech creates future legacy code - either from failed experiments or successful implementations that become outdated.

Most teams struggle with these practices, leading to an increase in "legacy" code.

Craft Solutions, Not Problems

Imagine moving into a new house, only to find it cluttered and confusing. You wouldn't want to deal with someone else's mess, right?

The same goes for code.

Messy code is code that's impossible to hand off. It's like leaving a maze for someone else to navigate. This can come from a few things:

  • Poor Design: Sloppy architecture makes it hard to see how things connect.
  • Missing Tests: Without tests, how can you be sure the code actually works?
  • Trend Chasing: Blindly following trendy practices can bloat your code with unnecessary complexity. Think "design patterns for everything!" or overly verbose architectural patterns.

Here's a good trick to avoid creating coding headaches, ask yourself:

"What's the simplest solution that gets the job done?"

Only add complexity when absolutely necessary.

Love learning new tech?

Great!

But don't cram it in just for the sake of being trendy.

Before diving into fancy design patterns, ask, "What specific problem will this solve?"

If the answer is "I just want to learn design patterns," hold off.

Focus on solving actual needs, not chasing the latest development trends or your learning interests.

Innovation Isn't Just Using The Latest Tech

Many engineers equate innovation with simply adopting the latest technologies - new frameworks, shiny new languages, and other stuff.

This misconception often backfires, creating more problems than it solves. For instance, if we suddenly decide to build all new services in Golang, our existing Javascript-based systems instantly become "legacy," even if they're still perfectly functional.

True innovation is about tackling new challenges or finding better solutions to existing ones.

Simply switching to a new technology without a clear purpose isn't innovation - it's just spending resources unnecessarily. This kind of "innovation" doesn't solve problems; it creates them, ultimately wasting company time and money.

To be clear, I'm not saying we should never adopt a new language or framework. The key is that the new technology must address a real problem, and it should be the optimal solution for that problem.

For example, while developing native mobile applications for both iOS and Android requires separate codebases (Swift/Objective-C for iOS, Java/Kotlin for Android), using cross-platform frameworks like Flutter or React Native allows developers to write a single codebase that can be deployed on both platforms, significantly reducing development time and cost while often achieving near-native performance.

Empowering Others

Remember how I mentioned code becoming "legacy" when the original creator moves on? The truth is, that most programmers can't write perfect code, but almost anyone can explain what their existing code does.

And guess what?

Teaching isn't just good for the learner, it benefits the teacher too!

Explaining things forces you to think critically and deepens your understanding, making you a better engineer. Plus, mentoring is a key skill for software engineers, so working on it helps your career path.

The best way to teach? Pair programming!

It's amazing how much a new engineer can absorb in a two-hour session fixing a tough bug alongside you. Also, you can pair on new features development - that way, ownership is shared from the start.

Next best option? Code reviews.

While slower and asynchronous (meaning it happens over time), they still provide valuable feedback. Written communication can be tricky, so you need reviewers with a keen eye and strong coding sense - not always easy to find.

Not All Oldies Are Broken

The term "legacy" should be reserved for systems that baffle us and resist change. If a system is simply old but functions well, labeling it "legacy" is unfair, potentially driving unnecessary rewrites.

Actively developed systems don't need a special label. Systems with slow or stalled development can be considered "in maintenance mode."

The core issue with "legacy code" is its maintainers' lack of understanding. Instead of throwing it out, we can simply learn its ways.

Even if it's not the prettiest code, incremental improvements, new features, and even adding tests are all achievable. While it might not be as exciting as a complete rewrite, it benefits the company more in the long run.

Only in rare cases are legacy systems truly beyond saving and need replacement.

However, such a decision can only be made with a deep understanding of the system and its issues. That means dedicated efforts at "understanding the system" for a significant period before a rewrite can be seriously considered.

So, rewrite only if you strongly suspect fixing the existing system will be much more work than starting fresh.

However, reaching that level of certainty requires a thorough understanding of the current system and the cost of building a new one.

Confront Or Evade

Legacy code requires attention, but that doesn't automatically mean it's your responsibility. Changing projects or even companies is a perfectly respectable choice compared to starting a rewrite.

If you decide to stay, the task might seem daunting and endless.

However, you'll likely be surprised by the progress achievable within a year. Furthermore, involving junior team members and gradually handing over ownership can be beneficial.

What feels like tedious, unrewarding work for you might be an exciting learning opportunity for someone newer to the field.

Legacy systems are present in most companies. They're a result of poorly written code left behind by previous engineers. The next person in line then lacks the resolve to address the existing issues, opting for a complete rewrite, repeating the cycle.

You have the power to disrupt this pattern.

By avoiding the creation of new legacy systems and gradually understanding those you encounter, you'll become a more proficient engineer.

Your team, company, and the market will recognize you as a problem-solver, not a problem-creator.

You'll be the one who brought order to the chaos, not the one who succumbed to it. That's the kind of hero Gotham needs.