Most of us aren’t lucky enough to spend our programming lives working on so-called “green-field projects,” i.e., ones that allow you to create the initial code. Since 2000 I’ve probably spent ten years working on existing code and combating (as well as contributing to) technical debt. Finding and fixing bugs is almost a rite of passage for new developers when they on-board with a new company.
Technical debt arises for many reasons—whether moving goal posts, pressure to get code tested and released, high programmer turnover, and lack of documentation. Whatever the reason, it’s likely to bite you if you aren’t careful. So what can you do to ease its burdens? Here are some suggestions.
Understanding someone else’s code can be hard. A useful technique on the way to comprehension is refactoring, or restructuring existing code without changing the code’s outputs; many modern IDEs (Eclipse, IntelliJ, Rad Studio, Visual Studio) have some great tools for refactoring in a way that allows the developer to improve maintainability and internal architecture.
Although I stopped using short variable and function names a long time ago, I recently had to reuse some very old code, experiencing a “Did I really write this?” moment in the process. Refactoring the code made it much easier to read. While giving variables and function meaningful names is simple, that sort of technique is really only relevant to older code; in many cases, it’s more useful to restructure, starting with breaking down huge monolithic functions (typically 300 lines of code or more that can be reduced into a smaller number of functions or even into classes).
One problem with refactoring large functions in languages such as C++ is the local variables. If you have a large quantity of code using local variables, it gets harder to split code out. Remember, the key to refactoring is that it mustn’t introduce new functionality; just simplify the code and don’t introduce new bugs. If you have unit tests, run those tests after each refactoring.
Moving to OOP and/Or Functional Styles
You can write C++ in a purely procedural style, though it can quickly become hard to maintain once your program grows beyond a few hundred lines long. Even small utilities benefit from using classes to hold and process the data—in addition, you’re less likely to forget to free up memory variables if you use the RAII programming idiom.
With Lambda expressions becoming commonplace in several programming languages, there’s much to be gained from using a functional approach; it complements OOP (and procedural code) rather than replacing it. C# has had LINQ for a long time; you can replace for loops and remove the chance of bounds errors using foreach to process lists, etc. Any function that returns a value without causing any side effects could be called functional, but lambda expressions (anonymous functions) are even more so.
Use Compiler Inference
C# has had var for a while, just as C++ 11 introduced auto for declaring variables. Letting the compiler infer the variable type reduces the need for typing (or copy and paste), but it can greatly shorten declarations and increase code readability. The simple example below shows the difference between declaring the type explicitly and then using var.
public static void Main(string args) l
ZipFileArchiver zf = new ZipFileArchiver();
public static void Main(string args)
var zf = new ZipFileArchiver();
Depending on the project, you may not have time to add unit tests; but with testing, it will ultimately take less time to debug. Give them a shot—the boost in confidence you receive when your code passes all tests is amazing.
Is the Code in a Version Control System?
Though they’re more about managing code, the tools provided with Version Control Systems can easily let you see what changes you made in coding, and thus help you undo the changes if your attempt at simplifying merely introduced bugs. (Some can also help you merge changes quickly, as well; a VCS isn’t just for code safety and sharing between teams.)
For developers, fixing others’ code goes with the territory, usually long after that person has moved on to another opportunity. With continual changes and improvements to most popular languages coming along, it pays to stay ahead of the game and use any changes and improvements to simplify existing code.