Every developer has been there: you inherit a codebase that looks like a bowl of spaghetti, and your first instinct is to say, “We need to rewrite this.” You see the outdated libraries, the inconsistent naming conventions, and the lack of unit tests, and you think, “I could do this so much better from scratch.”
But a rewrite is rarely just a technical decision. It’s a social and ethical one. Legacy code is code that is working. It’s code that is paying the bills, processing the transactions, and serving the users. When we dismiss it as “trash,” we are dismissing the context, the constraints, and the hard work of the engineers who came before us.
In this post, I want to explore the ethics of legacy code and why “rewriting from scratch” is often a failure of empathy.
The Hubris of the “Greenfield” Project
The allure of the greenfield project is powerful. It’s the promise of a fresh start, free from the “technical debt” of the past. We imagine a world where every variable is perfectly named, every function is pure, and the architecture is a work of art.
But this is often a form of hubris. We assume that the original developers were “bad” or “lazy,” when in reality, they were likely working under constraints we can’t see:
- Deadlines: They had to ship a feature in two days that should have taken two weeks.
- Changing Requirements: The business changed its mind three times during development.
- Incomplete Information: They were using a library that was “state of the art” in 2018 but is now deprecated.
When we start a rewrite, we are essentially saying, “I am smarter than the people who built this.” But six months into the rewrite, we often find ourselves making the same compromises, hitting the same edge cases, and creating our own brand of “legacy” code.
Code as History: The “Chesterton’s Fence” of Software
In philosophy, there is a concept called Chesterton’s Fence. It states that you should never tear down a fence until you understand why it was built in the first place.
Software is full of these “fences.” You see a weird if statement that seems redundant, or a global variable that “shouldn’t be there.” Your first instinct is to delete it. But that “weird” code might be a fix for a critical bug in a specific version of Safari, or a workaround for a race condition in a legacy database.
As I’ve discussed in my post on Code Archaeology, exploring a legacy system is like an archaeological dig. You have to carefully brush away the dirt to see the structure underneath. If you just bulldoze the site, you lose the history—and the hard-won knowledge—that the code contains.
The Empathy-Driven Refactor
Instead of a rewrite, I advocate for the Empathy-Driven Refactor. This approach starts with the assumption that the existing code was written by competent people doing their best.
- Read the Git History: Don’t just look at the code; look at the commits. Why was this change made? Who made it? What was the context?
- Write Tests First: Before you change a single line of code, write a test that proves the current behavior. This is an act of respect for the existing functionality.
- Modernize Incrementally: Instead of a “big bang” rewrite, improve the code one module at a time. This reduces risk and allows you to deliver value to the business while you clean up the technical debt.
- Document the “Why”: When you do refactor something, document the reasoning. Help the next developer understand why you made the choices you did.
The Business Ethics of Rewriting
From a business perspective, a rewrite is often a poor investment. While the engineering team is busy “rebuilding what we already have,” the competition is building new features. A rewrite can take months or years, during which the product is effectively frozen.
The ethical responsibility of an engineer is to provide value to the organization. Sometimes, that means swallowing your pride and working with “ugly” code because it’s the most efficient way to solve a problem. It’s about being a “pragmatic engineer” rather than a “perfectionist artist.”
Conclusion: Respecting the Bedrock
Legacy systems are the bedrock of our digital world. They power our banks, our hospitals, and our infrastructure. They are not “trash” to be discarded; they are assets to be maintained and evolved.
By approaching legacy code with empathy and curiosity, we can become better engineers. we can learn from the mistakes (and the successes) of the past, and we can build systems that are truly robust, not just “shiny.”
The next time you feel the urge to “delete it all and start over,” take a breath. Look at the code. Try to see the person behind the screen. And remember: one day, your “perfect” code will be someone else’s legacy.
Further Reading & References
- “Working Effectively with Legacy Code” by Michael Feathers: The “bible” of refactoring and testing old systems.
- “Refactoring” by Martin Fowler: The foundational text on how to improve the design of existing code.
- Code Archaeology: Exploring and Modernizing Legacy Systems: My deep dive into the methodology of exploring old codebases.
- “The Mythical Man-Month” by Fred Brooks: Specifically the chapters on the “Second-System Effect.”
- “Chesterton’s Fence” (The Collected Works of G.K. Chesterton): The philosophical origin of the concept.
Omid Farhang