Software maintenance is probably the most unglamorous task to be done in software. It gets no respect at all. In fact, it’s just a bit higher than software testing down the pit. Yet, this is what most of us do. This is what I do most of the time. It’s about working on code you have not written, or wrote so long ago you forgot you even did it. Starting a brand new project does not happen so often. In most cases, starting a brand new project is not even a good idea.
When writing any piece of code, maintainability should be the number one priority. No exceptions made. Code lives for a very long time – often longer than it’s contributors. Unless the project is very small and handled by a single person over it’s lifetime, it’s very likely that, at some point, the original authors will be gone and problems will be handed to someone else. Let’s face it, inner workings of software are complex and a ton of tacit knowledge required to understand it is never written, which is a good thing, because if everything was written in word documents, any significant piece of information would be buried in a big pile of information overload.
Here are some tips in maintaining and writing maintainable code:
1. Never modify code you don’t understand
No, this does not mean you should not make the modification. People patching blindly in existing code is the number one cause of degradation. Degraded code is harder to maintain for you next time and even harder for the ones after you. Unless you understand the implications of what you are about to do, go back and study a bit more. Print out the code and read it. Annotate it. Understand it. It takes time, it kills trees, but it’s efficient.
Try to understand the intent of the original author. If the code is old and you know others touched it before you, identify their modifications. Most of the time, you will find defects along the way. Problems that lived in the application for a long time. Too much code is never reviewed.
I stand with those who believe schools should teach to read code before teaching to write code. Reading code teaches you a lot more about writing maintainable software. You learn what you don’t like to see. If you have the least bit of sympathy, you will avoid doing it yourself afterwards.
2. Review your own code and improve it
Good code is elegant. Good code reads like a book. Does your code read like a book? When reading, do you stumble across a complex condition and ask yourself what it does? Create more functions. Even if they are one liners. A named function reads a lot better than a three line conditional. Intent is clear.
Remove commented code. Remove debug information. Make sure indentation is perfect. Use blank lines to divide unrelated statements. Space helps reading. Books have paragraphs to separate ideas, so should your code. Long sentences are hard to read, so are long statements.
Some give hard rules to these guidelines. Lines should not be longer than 80 characters. Functions should not be longer than 20 lines. Variable names should be between 4 and 15 characters. Files should not be longer than 200 lines. Nesting level should be at most 3. The McCabe cyclomatic complexity should be no higher than 7. All these rules have good intentions, but they really only help reviewers in using static analysis tools rather than reading the code. The problem with these rules is that they always have exceptions.
I have no trouble reading a 100 line switch case. As long as a function has a readable pattern and a coherent view of the world, there is no problem. The real problem with long functions is that they often do too much and work at different levels. Make sure the abstraction level of the concepts manipulated in the functions are consistent.
3. Don’t write useless comments
Organization that enforce rules about writing API documentation on every single class, function or member variable tend to have the worst API documentation. Programmers are smart. They have tools to generate those comments, but the machine generated comments are simply meaningless most of the time. I’d rather have no comment at all than having a comment that does not provide me with the information I need. In the end, I will have to read the code, so it’s better that I don’t waste time reading the comment on top of it. What good is a comment telling you that the function returns the time if it does not tell you in which format and which timezone?
Good function and variable names often compensate the need to have comments in the first place.
Comments are code too. A useless comment sitting there will have to be maintained if the code changes. Comments that do not match the code is misleading.
4. Thinking too far ahead is your enemy
Less code is better. Don’t write code because it could be needed in the future. Write comments that explain how it could be expanded in the future if the urge is too strong. Most of the time, the person who will have to do it in the future will have constraints you had not anticipated. Running into code that is supposed to do something, but does not really do it, is a complete waste of time. The future developer will first try to understand the code, then figure out why it does not work, attempt to fix it, adapt it to fit the needs and spend hours debugging your untested design. Writing based on their own design would have worked much faster, but most people tend to trust the code in place. If you write something you never even executed and leave it there, the next one won’t know it does not work until they have a lot invested.
Your ideas for the future may be great, but keep them to yourself, because others have greater ideas in the future, and those ideas match reality.
5. Don’t be clever, especially when it comes to optimization
In most cases, performance does not matter. Especially in the web, there are so many things surrounding the piece of code you are working on, the relative importance of your piece is meaningless. As programmers, we tend to focus our world around our current task. It’s normal. It’s the only way code can be written. It requires deep attention. But don’t be clever. Clever code, no matter how smart it makes you feel when you write it, is just harder to understand for the next one. The next one would have to be as clever as you are to maintain it (or as mentally twisted).
Performance always seems to be one of those qualities we want to have, but obtaining it requires a lot of clever tricks, and those hide the original intent of the code. Unless you have evidence from a profiler that a piece of code has to be optimized, keep it readable, it might even make it faster. Optimizing without evidence tends to reinforce myths about performance, and like all myths, most of them are false.
6. Refactor as you go
A lot of changes can be made to improve code readability without altering the behavior. If you have to modify code and it’s unreadable, start cutting it apart right away. If you do it well, it won’t hurt and it will likely help you make your modification later. Code rarely kills people. If you’re scared to modify code, it’s probably because there is something terribly wrong with it, so start making innocent changes to clarify intent. Most of the time, the base design is not all bad. It was just degraded by years of blind patching.
Bad code becomes better by making small changes continuously, not by rewriting it all. Rewriting is mostly about wasting time and remaking the same errors that were made in the past. A lot of knowledge is hidden in code. If after understanding the code and understanding it’s value you end up with the conclusion it’s fundamentally wrong, then you are ready to rewrite it. But don’t rewrite because you are too lazy to understand.
Spend less time complaining about code quality and more time improving it. If you’re not happy with the state of things, make sure your own work at least respects your own standards. It should encourage others to raise the bar too.
Maintaining code forces you to read code. Reading code opens you to new paradigms, new ideas, and makes you a better programmer. It’s a much harder task writing code alone.