I have seen and read numerous books and articles that cover new development, including design and architecture, coding, and project planning and management. Not so for humble and overlooked maintenance programming. Maintenance is often mentioned in studies on software life cycle, but actual design and coding techniques are usually taught with active development in mind, rather than maintenance.
Here, I am not going even to try to define maintenance programming, or to quote someone else’s definition. Instead, I’ll just list some characteristics of a maintained software as opposed to an actively developed one:
- It has been deployed and used.
- The original developers may have left the organization or switched to other tasks. There are less people involved in maintaining the product than developing it.
- Expectations from maintained software are generally lower. Maintenance usually includes fixing bugs as reported by users, and adding some features, but it is not the “flagship”.
- Maintained code is often developed with legacy or even unsupported technologies. The quality of code has usually deteriorated due to last-minute bug fixes and other hacks.
Maintenance Programming Good Practices
The tips I offer here are the result of my own experience. You may find they don’t apply well in your situation, or even more likely, that some of them apply and some of them don’t. If you have any additional advices, please post them at the article’s forum.
1. Get to Like It
How can anyone like maintenance programming? Developers dream of being chief architects in their teams, and when they end up maintaining existing software, it feels almost like punishment. It doesn’t have to be: maintenance programming has its own set of challenges, and requires a lot of creativity, adaptability, patience, discipline, and good communication. It can be good for your career as well: having bombastic entries like “Architected n-tier 24/7 enterprise application” in your resume looks nice, but employers actually value people who solve problems; and maintenance programming can be a good chance to demonstrate your problem-solving skills.
2. Get Familiar with the Functionality of the Software You Maintain
It is crucial to know the functionality of the software you maintain. Read the existing documentation, and make sure to try it for yourself. If you have a QA department, try to dig out the test plan, and go through it. Find out how the software is being used in practice and make sure you know the most common scenarios of use. There will be cases when users come to you requesting features that already exist, they just don’t know about them. In other cases, there will be handy workarounds. If you are a power user of the software you maintain, chances are you will spend less time in the debugger.
3. Get Familiar with the Architecture of the Software You Maintain
You would expect to inherit detailed and up-to date design documentation, including requirements, functional specifications, high-level and low-level design, as well as well-commented code, right? In reality, chances of that happening are close to zero. It is much more likely you get a little, if any, documentation, and even that will probably be outdated. Code, or some parts of it, may be commented, but you can’t be sure even the comments are up to date; sometimes they can actually be deceptive if the code was changed in the meantime.
Look at the code; try to understand the roles of classes, modules and components. Use the debugger: step through different scenarios of use, and see what happens when different parts of code are executed. Be sure to write down your findings and organize them in some form; it can be some formal method like UML diagrams, or something informal but easy to understand. Think of getting familiar with the architecture as of an ongoing process, rather than one time thing. You will have the opportunity to know the system even better when you start to fix bugs and add features; be sure to document your findings in this stage as well.
4. Communicate with the Users
I can’t stress enough the importance of this. Many software developers are introvert persons who prefer to deal with technology rather than with people. However, the software is written for people to use it, and in the case of maintained software, we already have users. It is crucial to find out how they use the software, why they use it, what problems are they trying to solve, what functionality they need. To find that out, don’t wait for them to come to you; be proactive and go to them. Try to establish simple and effective procedures for submitting bug reports and enhancement requests. When they report a problem, get back to them immediately, even if you don’t have a solution at that moment – just let them know you are looking into it and not ignoring them. Keep them updated about the status of their problem. Finally, be honest; if you can’t meet their request for any reason, tell them.
5. If Possible, Communicate with Original Developers
If you are lucky, some of the original developers are still with the company. Sure, they have moved forward and want to forget about this old crappy software you are maintaining, but they can be a tremendous help and save a lot of your time. Don’t expect them to do the actual coding for you, but they can throw you an idea how to accomplish something, explain you why something works the way it works, and provide you with lots of helpful hints. Again, dealing with them requires “people skills” that many developers lack, but it is not something we can’t learn.
6. Keep the History of Changes
There are different and not mutually exclusive ways to keep the history of changes. The most obvious thing to do is to comment each and every check-in to your source control system (I am not even going to consider the possibility you don’t use a source control system). Some people prefer to put the list of changes at the top of each file. It may be convenient to have a centralized list of changes in a spreadsheet as well. Whatever approach you take, be serious about it. An accurate history of changes is invaluable for successful maintenance, even if nobody else before you took care of it, and you get only a partial list of changes.
7. Be Conservative
Many times during the maintenance work, you are going to get frustrated with the existing code in some module, and just want to toss it away and rewrite it from scratch. Before you actually do that, ask yourself some questions:
- How would it benefit your organization and its customers? The fact that some code is ugly, or looks ugly to you is not per se a reason to rewrite it. If a customer wants a feature that can’t be added to existing code, that could be a reason to consider rewriting it.
- Are you sure you understand what this code does and how it works?
- Are you sure you understand how it interacts with other parts of the system, and which modules and functionality depend on the code you want to rewrite?
- Are you sure you can make it better? Maybe you run into the same problems the original developers did, and make it same or even worse.
The fact is that whenever we make a change to existing code, no matter how trivial the change is, there is a risk of introducing bugs. Therefore, we want to be as conservative as possible given the circumstances. When a new feature is requested, we would want to implement it in a separate module if possible and keep the old code unchanged. Don’t fix it if it isn’t broken. If it is broken, fix it rather than rewrite it from scratch. Keep the changes as local as possible – you don’t want to find out that you improved one part of the system and broke another.
Refrain from refactoring. While it can be a useful technique during active development, in maintenance phase, it will probably bring you only troubles. Even if you don’t break anything through refactoring, you will complicate your history of changes and screw up the differences between versions in your source control system (you do have a source control system, right?).
Also, when you do change the code or add new features, try to do it the right way immediately. Too many times I’ve seen people say “let’s just make a quick hack now to meet the deadline, and we can rewrite it properly later”. Well, “later” usually never comes, for various reasons. If you can, do it properly; if not, be honest with yourself and don’t mention “later” as an excuse.
8. Test after Every Change You Make
Probably the first coding task you need to do after taking some software to maintain is to write a comprehensive regression-test suite for it; that is unless you inherited one already, but in general, you are not going to be that lucky. It is true that automated tests cannot guarantee the code is bug-free, but it is a poor excuse to avoid them; they will not uncover all bugs, but they will help you find many and bring you confidence that the core functionality of the software you maintain is not broken after you do a change. A good practice is that for each defect report you get, write a test-case to reproduce it. That way, you will know if you reintroduce a bug when fixing something else.
Sometimes, depending on the maintained software, it may take too long to run the whole regression-test suite after every little change you want to check-in to your source control system (which you do have, right?). In that case, it may be useful to take a subset of regression tests and make a “smoke-test” that would cover only a subset of test cases from the regression-test suite. You would run this smoke-test after every change you make, and the regression-test overnight or during weekends.
9. Adopt Existing Code Conventions Even If You Don’t Like Them
Code conventions are something people get religious about, and in general, it is hard to make someone change his mind about which coding conventions to use. Even with languages that come with suggested coding conventions from the authors (like Java, Visual Basic and C#), developers still argue about them. With languages where authors leave the conventions to developers (C, C++, Perl), the matter of choosing a convention quickly becomes a war.
I know I am not going to convince anybody, but try to stick to whatever conventions exist in the code you maintain. Even if there is no uniform convention all over the system, chances are that the conventions are uniform at least on module level, since in most cases, one person develops one module. In the worst case scenario, someone other than the original developer already altered the code and used other conventions, so you have a horrible mix of conventions to deal with. Even in that case, adopt one of the existing conventions, and don’t introduce yet another one.
Maintenance programming has its own set of challenges, and these challenges need to be addressed in a disciplined manner, just as with active development. In this article, I present some practices that I found useful in my work; this is by no means an attempt to write the definite guide to the art of maintenance programming.