It’s day one at your new company. The tour of the company is complete and your boss has settled you into your workspace, complete with state-of-the-art computer equipment. He provides you with the credentials needed for you to peruse the lines upon lines of beautifully written, elegantly documented code. You happily dive in and make changes that improve speed, quality, and scalability. Although they are minor improvements because the code is so sexy and well written, you still feel like a productive and valuable part of the company.
As you remark to yourself just how ideal this situation is, you feel a hand on your shoulder: “Hey wake up, you were drooling in your sleep.”
For most of us, the reality is our new employer is at his wit’s end because the former developer left without notice and no one has a clue where to pick up where she left off. A deadline is looming and it’s your job to adopt this abandoned code and get it to cooperate – and in a timely manner. The former developer? Nowhere to be found, or, if you track her down, wants nothing to do with the project. Documentation was an afterthought and never got done. The code is riddled with question marks and indelicate expressions of displeasure with the code, the boss, or life in general.
Although you might be inclined to drink yourself silly when you open up that code base, here are a few ways to salvage your own sanity.
Start by looking at the big picture
“It’s harder to read code than to write it.” –Joel on Software
It’s in our nature as programmers to make a beeline for the code base. We want to dig in and make improvements immediately. Unfortunately, when we inherit code from another developer who left in a hurry (due to greener pastures, boredom, or that regrettable incident with the Twitter update) we also find ourselves at a disadvantage because we weren’t present when key decisions were made.
Although it may be a challenge to your logical process, the most advantageous process is to step back and take in the entire project from a wider vantage point. Make it a point to talk to the stakeholders first. Have them provide you with an overview of what the software is supposed to do. Ask for design documentation, functional specifications, and user stories. Before you touch the code, go through any repository and revision systems to get an overall feel for their process (if there is no versioning system, now is the time to implement one!) If a versioning system is in place, go through the commit logs; they help provide a chronology of development.
Approach the software from the end-user perspective and get to know every single nook and cranny. Ask questions. Make notes. Clarify expectations.
Consider: Scrap it and start over?
“It's important to remember that when you start from scratch there is absolutely no reason to believe that you are going to do a better job than you did the first time.” – Joel Spolsky
Many programmers new to a project assume that it was written poorly, when in fact this was the previous programmer patching and hacking the code to meet end users’ demands. Try to walk in your predecessor’s shoes. Look at the entire code base and its intended functionality before you take it apart and possibly rebuild it.
It’s better to work with the current code base as much as possible, unless you are making a major systemic change, such as moving to a new type of language (from compiled to Object Oriented, for example) or you are dealing with single threaded legacy code that needs to become multi-threaded. It makes more sense to deprecate code and replace with stable code than to wipe out an entire code base that is working.
You can kill a project by attempting to rewrite the code before you fully understand it. Conversely, by the time you do fully understand it, there won't be any need to rewrite it, because you'll own the code.
Suck it up and deal
For the most comprehensive understanding of your newly adopted code base you can’t beat looking at it line by line. Patterns will emerge that’ll give you a deeper understanding of both the former developers’ intention and their process. Even when you inherit a code base with no traditional documentation you may be able to translate “here is what I’m doing” from looking at individual functions, function names, and patterns.
Going through the code line-by-line can be the worst case scenario, especially if the code base is hundreds of thousands of lines. On the other hand, if you are truly left with no other recourse it makes sense to go through the code in this manner to grasp the intricate details. This is where regression testing (functional and unit tests) and diagraming come in handy.
Create regression tests
Getting to know your inherited code base involves testing it to see what it actually does (as opposed to what the rotten documentation claims it does). One risk with making code changes in one area is breakage in another area.
After you’ve made a copy of the entire code base and saved it to your favorite repository, you can poke, prod, and otherwise violate the code to see what it does and begin to understand how and why. Using unit tests and functional testing tools to automate testing and monitor results provide an understanding of the intention of the code.
The downside is that regression testing can be costly in both time and resources. You may need to utilize this process in a manner that protects against degrading the suite and breakage in other areas. In other words, don’t overdo it. Use regression testing both manually and automatically and weigh the benefits of the results with the time it takes to create and repeat the tests.
Find a big whiteboard
A simple diagram is worth a thousand words – or a thousand lines of code. Visualizing the code base through diagraming allow us to abstract and grasp things easier than listening to someone explain it or from us painstakingly traversing the code. Using a white board or other diagramming system is pretty common practice at the start of a project but this works just as effectively when you inherit code.
Diagramming allows you to see all the relationships between functions, subroutines, and objects. This goes back to actually seeing the “big picture” (when you physically step back and look at the white board). You’ll discern what could be causing a specific bug or inaction when the software flow seems wrong or doesn’t make sense.
Each situation with code inheritance is different. Some developers are lucky enough to walk into a situation where the former developer kept potential future developers in mind; but you have to start a new-to-you project with a more cynical attitude. Assume that you need to do some work before you can actually make significant changes to the code base.
But take heart: Even diabolical inherited code can be tamed.
Do you have any nightmarish code inheritance stories to share? Strategies for how you or a colleague overcame these difficulties? Feel free to share with us in the comment section.