Every developer has a complicated relationship with legacy code. We often call it technical debt—a messy, bloated monument to past decisions. The original authors, whose names we might find in the commit history or old code comments—if we're lucky enough for those to exist—are often loathed and given affectionate names like "he who shall not be named". We want clean code —the greenfield project where everything is awesome, modern, and aligned with the latest framework. But what if that perspective is somewhat flawed? What if technical debt is not a mistake, but the inevitable reality of building software? This is a history of my experience over the past two decades, defined by the evolution of the companies I've worked with. The aim of this article is to hopefully change our view of legacy systems—rather than seeing them as these mistakes we should be ashamed of, we should instead view them as a natural part of system evolution that we as experts are required to navigate and evolve.
I like to think of these codebases as Geological Strata. In geology, strata refers to the distinct layers of rock that accumulate over millions of years. Each layer tells a story about a specific period in Earth's history, reflecting the conditions of that time. Just as the Earth's history is written in its rock, so too is the history of a company's evolution recorded in the layers of code, technologies, and methodologies that have accumulated over time. My time has largely been spent inheriting these very systems, and I have contributed no small amount of debt that developers for years to come will no doubt loathe.
Here is a view of how I’ve seen the layers of software laid down over the last two decades of my life. I first started in 2007, where I inherited a largely procedural Property CMS hosted on a bare-metal server. We were literally overriding one another’s code using FTP/Rsync/SFTP to get our software to production. It may come as a surprise, but even back then, Object-Oriented Programming and Version Control was still a new concept in many companies.
Later, I joined a new squad where my first task was to move a similarly structured, decade old procedural CRM to support version control (GitHub was not always a thing). Those where many late nights refactoring "all-in-one" 10,000-line files—a mix of HTML, CSS, SQL, JavaScript, and Server Side Code—into a more object-oriented style.
Cloud computing was becoming popular, which introduced another big change in how we build and deploy software. By 2011, this with MVC became a major trend in many companies, with frameworks like Cake, Symfony, and Ruby on Rails finding its way to the late adopters. After resistance about the perceived bloat of these frameworks, I ended up building a custom one that, in hindsight, had even more bloat. This was all in an effort to break apart our monoliths into microservices, testing everything from SOAP and XMLRPC to eventually settling on REST, which is still the flavour of the day. I fondly recall talks at a conference where the slowness of network hops of API architecture was the main topic as every new layer brought its own novel problems. Over the next period, more layers were added: the introduction of sockets for non-blocking operations, message brokers for background processing , an aggressive push for mobile-first development, and between all of this JavaScript had somehow taken over the world.
In 2020 a colleague introduced me to ChatGPT a full 3 years after it was launched (Epic fail). These LLMS are now really just is the next layer of strata in a long line of methodologies. Suddenly, we were talking about AI generating code and I was presented with the most interesting challenge yet: to have these specialized agents generate fully integrated systems in complex domains. I was initially skeptical, citing that the technology was just not there. In those early days of AI (last year), we began by calling the API and extracting code from responses with Python Regex, writing them to disk. Despite my disbelief, we successfully launched a pilot that processed tens of thousands of requests over three days. Now we have Agents writing enormous amounts of code, geared to take over the world.
This experience was gathered over a dozen companies in completely different industries, experiencing the same evolution. Let's be clear: while I hold that your code is legacy the minute you hit ctrl+s, I am not advocating for preserving the status quo and saying that nothing can be done to improve legacy systems. My experience inheriting these systems across several companies has just shown this to be the inescapable reality of our work. While we should make every effort to manage our technical debt, our job is really to be able to effectively navigate this and find a way to evolve the system. So I am not a big fan of technical debt sprints, I believe that this is just our day job. The true test of a developer is not just their ability to build something new on a clean slate. It's their ability to continuously evolve these complex layers to meet the organization's and competitive landscape's evolving requirements so these companies can endure for decades to come. I believe that the value we bring to the table is our deep technical knowledge and problem-solving ability to understand the history, navigate the strata, and figure out how to evolve the next layer without causing an earthquake.
