What If Legacy Code Is Good, Actually?
M. Scott Ford wasn’t enjoying his job as a software developer — but not for the usual reasons. He liked working with computers, and had been a developer at a number of companies after graduating from Virginia Tech with a degree in computer science. But at each job, when he felt he was making things better by cleaning up the codebase, he would be told to stop wasting time and get back to work. So he would move on to the next job, hoping for an environment more conducive to the type of refactoring work he enjoyed, only to be thwarted again.
“The improvements weren’t seen as valuable,” Ford says in the inaugural episode of the podcast he co-hosts, called, improbably, Legacy Code Rocks!
As Ford learned through experience, companies aren’t keen on making code look pretty. Best practices for software development dictate clean and readable code, but in business, generating revenue is what counts. To business leaders, that usually means more code, not necessarily good code. But companies may pay the price later if the software needs changes, the original programmers are long gone, and the code is indecipherable.
One day, while Ford was watching an episode of the home-improvement show This Old House, he had an epiphany.
“I was like, ‘That’s what I want to do!’” he said in an interview with Built In. “[My business partner] thought I wanted to quit my job and remodel houses. No — I wanted to do what they were doing for houses, but I wanted to do it for software systems.”
Ford realized he wanted to work exclusively with legacy code.
Who Would Want to Work on Legacy Code?
Ford’s decision was unusual, to say the least — legacy code doesn’t have the best reputation. By definition, it refers to software built years ago, often in older languages few developers are familiar with. When companies find themselves suddenly needing to make changes after a number of years, the task lands on the desk of some poor developer who has never seen it before, with no one around to guide them or help them understand how it works.
Some developers hear “legacy code” and start searching for a new job.
Ford saw it differently. He enjoyed the parts of software development that, to him, felt like detective work — the untangling of spaghetti code, the excavation of buried libraries and deep dives into nested method calls.
Andrea Goulet, Ford’s business partner and podcast co-host, recounted on Legacy Code Rocks! the time when they worked on a customer’s legacy project that had no documentation, as far as anyone could tell, and was written in an in-house programming language. Your basic programming nightmare.
“I have never seen you so happy,” Goulet told Ford. “You were like a kid at Christmas, diving into the binary files.”
Untangling the History Behind Legacy Code
While watching a reality TV show about home renovation may be an odd way for a software developer to discover their life’s calling, Ford sees the satisfaction he feels working on legacy code mirrored in the projects the This Old House cast tackles each season.
“I was watching the way that they work on older houses, and the amount of care and craft and love that they put into the infrastructure that they’re working on,” he said. “The way that they would talk about the people who worked on the house before — how, even if something wasn’t done to present standards, the way it was done was the right way to do it then.”
Where others see an intimidating mess of code, Ford sees an exciting challenge, and an opportunity to make improvements that would benefit a lot of people, including customers, users and even developers.
“For legacy systems that have just accumulated a ton of technical debt, or they’re just a bear to work with, there are a lot of people whose lives are impacted by the state of that system,” he said. “It’s kind of like you’re making someone’s life better.”
“It has value, it’s doing something meaningful, it’s solving an actual problem.”
Ford also takes satisfaction from knowing that every legacy project he works on is truly valuable to its users — after all, if it weren’t, then no one would bother working on it.
“Generally, if you’re working on something that’s been around for several years, and the company that built it is looking to invest in making it better, then it’s providing somebody value,” he said. “It has value, it’s doing something meaningful, it’s solving an actual problem.”
Ford’s enthusiasm for aging infrastructure is similar to what someone who finds hardwood floors hidden under carpet in a 19th-century home might feel. For him, “legacy” takes on its original meaning of inheriting something of value.
“Anything that someone has left behind is their legacy,” Ford said in the podcast. “It’s taking something that someone else has left behind and perhaps given up on, and breathing additional life into it, and really kind of pulling out its inner beauty and its potential.”
Of Course, Sometimes ‘Legacy Code’ Just Means Bad Code
Ford and Goulet began working exclusively on legacy projects a couple years after starting their software consulting company, Corgibytes, in 2009. (Tagline: “Old code, new tricks.”)
Although the term “legacy code” evokes decades-old software, the company sees plenty of demand from projects built with modern programming languages as well.
“There’s two major camps,” Ford said. “One is they think of COBOL and Fortran, and they think mainframes — like really old, ancient stuff. And there are other people who think of the stuff they wrote two weeks ago, that’s just impossible to work with.”
In many of the projects Ford takes on, end users may be frustrated with bugs, the amount of time spent waiting for improvements to the software, and newly released versions that don’t work right. At the same time, developers are frustrated working with the code and with areas of the codebase they are afraid to touch.
“If things aren’t created in an ideal way, I think it comes down to the constraints that people are under.”
“Features used to get created and shipped quickly, and now it seems like each successive feature is taking longer and longer — that’s a problem we hear quite a bit,” Ford said. “Another is retention is hard, morale on the project is low, people aren’t enjoying their work anymore.... They just can’t keep up with the bugs, or each time they release more bugs than they had the last time.”
These types of conditions can lead to tense working conditions, which Ford said could further exacerbate the situation.
“We make it very clear that our job isn’t to come in and point fingers, and say that the people who worked on the system didn’t know what they were doing or they’re bad people,” Ford said. “If things aren’t created in an ideal way, I think it comes down to the constraints that people are under. One constraint can be knowledge — you’re a junior developer who only has a couple years of experience, and there’s a lot you haven’t been exposed to yet. Or you’re an executive who really, really needs a capability out of a system, and you don’t understand why it’s taking so long to create it.... These constraints and motivations can create an environment where less-than-ideal code gets created. And it’s understandable.”
Frustration with an existing project can cause developers to make shortsighted coding decisions, but real improvements require an understanding of the codebase, which can take time and effort.
“You wouldn’t bulldoze your house just to give yourself a new kitchen,” Ford said. “But I feel like there are many engineering teams that will do that, because they’re frustrated with their build systems.”
D.I.Y. Code Renovation Tips
Like any good home-improvement show, Corgibytes kicks off every new project with a thorough inspection.
“We do a really deep dive into the codebase, where we look at different static analysis metrics that we can measure,” Ford said. “And it’s like a 30-ish page document, it takes us four to six weeks to put together.”
The team uses static code analysis metrics — including code complexity, duplication, coverage and churn — to determine which parts of the codebase need attention and to prioritize its efforts. The metrics are considered holistically, so code that has high complexity may still receive a low priority if it doesn’t get changed very often.
The inspection covers processes and team dynamics as well.
“We also look at different team practices — are there code reviews? Are the deployments repeatable? How is source code control being used?” Ford said. “Your different engineering practices, different DevOps practices, your communication styles.... Kind of look at the people part of a team and also the product that’s being created, and how those come together to tell a story.”
One strategy Ford recommends to companies is deleting code from the codebase that is no longer being used, which he refers to as “dead code.” Just as rotting wood can damage the structure around it, dead code can negatively impact software infrastructure and use resources that would be better spent elsewhere.
“We’ve seen some systems where there are large sections of the codebase that the engineering team is making sure is continuing to work — the engineering team doesn’t realize they’re not being used anymore,” Ford said. “The cost is really just the effort involved in making sure that everything stays working ... regardless of how you’re testing it, whether you’re doing it in an automated fashion or manually. For teams that are doing it automatedly, I feel like it happens almost more often — ‘Oh yeah, the test broke, so we have to fix them.’”
“Anytime you make a change, that change will introduce some complexity, unless you fight it.”
Ford also recommends keeping things at minimum “mess neutral” whenever developers go into the codebase to make changes. Although legacy projects are often seen as inherently cursed — messes that somehow are still running and hopefully will hold together another few years — Ford said software that’s in use is always going to accumulate technical debt, even if developers aren’t actively working on it.
“As long as it’s being used, there will be this pressure to change,” he said. “That pressure will come from either the environment that it’s interacting with — third-party libraries, or user interfaces, or the operating system that it’s running on — or it could be user expectations: layout, how fast is it, is it providing the right functionality.”
These pressures will only go away when the software is retired. Until then, the question is how development teams will handle the changes these pressures eventually require. Ford recommends developers do some clean-up and maintenance in exchange for every change they make to a legacy system.
“Anytime you make a change, that change will introduce some complexity, unless you fight it,” Ford said. “So I think the moment you’re making a change is when it makes sense to do a little bit of clean-up, and at least make sure your change is mess neutral. ... Don’t just do the quick fix, don’t just slap another Band-Aid on the system.”
For instance, developers responding to customers’ feature requests may be tempted to add features without understanding the code around them, but that kind of change adds complexity to the codebase. Instead, developers should look for a way to improve the system and make it easier to work with for next time, Ford said.
“Make it more performant, or make it easier to add a feature,” he said. “Or if there are no automated tests, add some automated tests. If it’s not in source control, put it in source control.”
Are You a ‘Maker’ or a ‘Mender’?
On the podcast, Ford and Goulet distinguish between “makers,” a common term for engineers and developers, and those they call “menders” — developers who are more code-improvement inclined, like Ford.
But, as they say in their podcast, there’s probably a mix of both in each developer. Makers can learn from the patience and care menders have for wrangling legacy code, and menders can learn a thing or two from makers as well.
“There’s no end to better, unfortunately,” Ford said. “If you only have people working on a project who enjoy cleaning up things that they see are problems, then they may just keep going on forever ... so that’s why I think that maker-mender energy balance can really help.”
Working with legacy code can be a frustrating experience, with unexpected setbacks and reversals
Ford said on home-renovation shows, “you see a lot of examples of people who cut corners over the years. They get into a house they’re like, ‘Oh my gosh, this subfloor is unsafe, this is going to cost an extra $2,000.’”
“There’s no end to better, unfortunately.”
“That’s another great analogy for what we often run into in software systems. We’ll start a project with the best of intentions for how long it’s going to take and how much it’s going to cost, but then we get in there and we notice, ‘Oh my gosh, there’s this other problem that we didn’t know about before we got started.’ And it has to take priority. And that can be really frustrating for everyone who’s involved.”
In situations like that, which come up often, Ford said it’s important to keep lines of communication open and be honest about setbacks in order to stay nimble with the changes.
“Take an honest look at it and just recognize that we can’t predict the future,” he said. “We’re all doing our best. Unexpected stuff happens all throughout our lives ... so to expect that everything’s going to go perfectly in our software projects just isn’t realistic or fair to anybody’s expectations.”
At the end, the feeling of refurbished software is its own reward. Legacy code that once seemed to resist change on every line can be transformed to work in harmony with modern technologies.
“That’s where some of the excitement can come in, is being able to use some of the newer technologies on an older system and finding ways to adapt those in,” Ford said. “For legacy code projects, the long-term goal is to make it feel like it’s new again.”