Working with legacy software

This was my first time working with legacy software and it wasn’t easy. I learned a few lessons along the way that I want to share.

The company I’ve been working at hired me in an effort to bring their application up to date on new programming techniques such as SOLID programming principles. The code I landed in is, in my view, a big ball of mud and each day I feel like I’m shovelling more mud on top of the heap (it stopped being a ball a long time ago). In the midst of all the legacy code, I learned a few lessons.

The first thing I learned is that most of the internal developers are not up to speed with current development techniques, principles and patterns. None of those programmers are writing bad code on purpose, but neither they nor management has invested in their coding skills. When I talk about SOLID principles, most of them have not heard of it, have not looked them up or haven’t had training. Yet somehow there is an expectation that the code that is produced will follow those principles.

On the other hand, there is an abundance of business knowledge among them. Several have in depth functional knowledge. These programmers are mostly the senior programmers. They wrote the older code in C and C++. They use coding techniques from decades ago and it has been ingrained into their thinking. So although their skills are not up to date, their knowledge about the product is spot on.
Know that when you’re going to work with legacy code, there will be a number of programmers with legacy code skills that don’t really want to change their ways. Learn to deal with them, explain new techniques carefully and take it step by step.
Some of those senior programmers will not understand those new techniques immediately or the benefits they bring. Do not be discouraged when they don’t get on board immediately. Over time, when more new programmers use the techniques (such as SOLID) and experience their benefits firsthand, they will convince the senior programmers to learn the techniques. If that doesn’t do the trick, the changing code base will either force their hand with new processes (such as pull request that get refused) or it will drive them out of the programming department.
That last outcome might mean that they leave the company. To prevent that somebody with a lot of business knowledge leaves, it might not be a bad idea to promote them to the functional team. With their knowledge, they might be more suited in a role that guides the features or teaches new employees how to use the program. Just be careful that they don’t try to influence the new way of working from their new position of power. That would be a typical dungeon master anti-pattern.
The second lesson I learned, is that there are secretly two types of legacy systems. I compare them to old houses. The first house is one that’s showing a bit of wear and tear: paint coming off the walls, broken windows, maybe a hole in the roof. The redeeming quality of this house is that it has a solid (pun intended) foundation and no major structural problems.
The other house is one that is rotting where it stands, the foundation is sinking or, in the worst case, missing, there is mold on the walls, the electrical wiring is a fire hazard,… You get the idea. There is no fixing this house. This house needs to be broken down and build from the ground up.
It’s the same with legacy software. There are projects that just need a bit of love and attention to get them back on their feet. Then there are projects that need a cleansing fire so a new and improved version can rise from the ashes.
The problem is that I can’t decide on which side of the fence this project falls. On one hand, I see a lot of redeeming qualities in the underlying structure. On the other hand, I see so many places for improvement that I think it would be faster and more cost effective to start anew. If (and only if) there are enough resources allocated for this rewrite. This means that there would be a budget for the rewrite, but also a dedicated team to program and guard the quality of the new software. Rewrites have been attempted, but key people (read: people with functional knowledge) have been reassigned halfway trough the rewrite. This isn’t the only problem that occurred, but I feel it is one of the more important problems.
The next lesson I learned is how much I’ve come to rely on automated tests, whether they’re called unit, integration or functional tests. Good, reliable tests are an immense reassurance when refactoring code. Press the little >> arrow to run all the tests (or auto-execute dirty tests when saving) to verify that everything works as intended. While the team did begin working on a test suite recently, it isn’t complete or entirely reliable yet. Each line of code I refactor that isn’t accompanied by a small army of tests, makes me doubt the quality of the result of the refactoring.
Lastly, I learned that I need a ton of patience to deal with all of these situations. Dealing with out of date developers, recognising all the area’s that can be improved, thinking of better solutions (both on local and architectural levels), thinking about a roadmap to implement those solutions and not seeing much improvement because everybody is busy keeping customers satisfied. It can be very tiring and demotivating.
Yet, the most important lesson I learned here is that developer skill determines the quality of the application. Everything else derives from that: bad architecture, lack of SOLID principles, gaps in the test suite, etc. Start with the people working on the software and work your way up from there.