There are a lot of books, blog posts, articles on programming languages, frameworks, design patterns for writing brand new software but not so much on debugging and maintaining legacy systems. This is surprising because a big part of software development is about maintaining legacy systems.
It’s understandable though. Legacy is not exciting but new is, so everybody wants to talk about new things. That’s great but there are a lot of developers in trenches like myself who spend a lot of time working on non-exciting yet essential work like maintaining legacy systems and I feel like we could use some knowledge sharing.
What’s a legacy system?
By legacy systems, I don’t mean some ancient code written in 1980s that you have to maintain (hopefully you don’t!) but rather I mean code that was designed and implemented before your time in your current role. I call these legacy systems from your perspective because you had no input into their design and implementation. You were basically late to the party where all cool design and implementation choices were made and now you are stuck with understanding and maintaining someone else’s thought and implementation flaws. If that person is still around, you’re somewhat lucky. At least, there’s someone to ask questions but most of the time, you’re left on your own to understand what’s going on.
Debugging is a prerequisite for understanding and maintaining a legacy system. No matter how simple or complicated a system might be, you will need to step through code to get a general feel for the system. My ex-colleague Jeff Vroom recently had a great post on debugging that covers everything I’d say on that topic.
Once debugging is done and you have a good understanding of the system, you need to fix/maintain the system. These are some guidelines I try to remind myself as I’m working with legacy systems.
Have the right mindset
As programmers, we are very particular about how we do things. So quite often, we find someone else’s code ugly, stupid, convoluted, hard to maintain, etc. See code as code only and focus on the problem you’re trying to solve. Nobody cares about how beautiful or ugly the code is according to you but a lot of customers would benefit from the bug fix or feature you’re working on, so make sure you remember that.
Keep bug fixes simple
When fixing a bug in a legacy system, try to keep it as simple as possible. The smaller the fix, the easier it’ll be for others to understand it and the easier it’ll be to merge to other branches. Even though the fix might not be theoretically perfect or up to your standards in terms of style, that’s fine as long as it covers the use case you’re trying to fix. In my experience, the benefits of easily understandable and contained bug fixes outweigh any style or theoretical considerations most of the time.
Add new functionality as a cohesive unit
When you’re working on a feature in a legacy system, try to add the new functionality as a cohesive unit. Don’t scatter your new feature throughout the system. If possible, create new methods within the class you’re editing, or new classes within the system. The more you isolate what you add, the easier time you and others in your team will have later. You’ll be able to test what you added specifically, you’ll be able to take out your code and see if a particular bug still happens and so on.
Resist the urge to refactor
I know if you were there for the initial design and implementation, everything would be better and I know that the code you’re looking at now is not what you want to look at. But despite all that, resist the urge to refactor unless it’s really needed. I’m not saying you shouldn’t change code at all. You should always follow “Leave the code better than you found it” principle but do it incrementally, so people can track what you’re doing. If you refactor bunch of classes in a major way all at the same time, nobody will be able to grasp what you just did. If you’re lucky, there are good tests to catch any errors you introduced but most of the time, that’s not the case, so it’s a big risk to refactor a legacy system in a major way at the same time because you will most certainly introduce errors. Instead accept the system for what it is and do incremental changes. If done consistently, small incremental changes over a period of time do wonders to your legacy code base.
Ask for help but strive for independence
Don’t spend hours and hours trying to understand the logic behind some piece of code, when the original programmer could explain everything in 10 minutes. I know it could be time-consuming and somewhat annoying for the original programmer to spend time explaining his code, but hey, you’re doing them a favor by maintaining their code, so the least they can do is to spend some time in explaining their code. Having said that, your goal is to gradually reduce your dependency on the original programmer. If you sincerely put in the effort to understand the system, it’s just a matter of time.