The Art of Maintainable Code

First of all, what do I mean by the term maintainable code? It is a term that is quite elusive to define, many have tried to quantify it with varying degrees of success. The difficulty is that it is composed from numerous factors, coming together in a complex way to get to a subjective conclusion. Ultimately though, maintainable code is code that another developer can pick up and work with without lots of head scratching, and that is very hard to measure.

Writing enterprise level code has to be done on the assumption that the person writing it isn’t going to be the only person that ever works on it. My current organisation has C/C++ code in production that is over twenty years old. It has been modified and changed over that time obviously but it was originally written in the nineties. Whilst still having code that old still being in production may be frowned upon by some, it really isn’t that unusual, and the idea that the code author will move on to other roles is the norm. I suspect most, if not all mature organisations that have been developing code for a long time still have code bases in use that were written a long time ago.

There are lots of advocates for never leaving code untouched for very long, and I agree with the sentiment but the reality is that many organisations do, for a whole host of reasons. More modern languages, the likes of Java, C#, GoLang and JavaScript that have long tails of dependencies of frameworks and libraries, make leaving code bases untouched very risky. They need to be revisited regularly if only to update those dependencies. The older code languages however didn’t have the package management capabilities of the likes of nuget or npm and quite often only use a couple of OS libraries, so leaving them untouched whilst not a particularly good idea, doesn’t hold the same level of risk.

Applications that sit and happily chunter away in the corner untouched will often only be changed when the hardware they run on becomes a problem, usually through security or maintenance concerns. When that happens, developers run for the hills, nobody wants to be the person responsible for taking twenty year old code and updating it.

“Oh yeah, old Joe wrote that, nobody understands it, it just worked.”

Words like that will chill the blood of just about any experienced Software Engineer. That code is probably the epitome of unmaintainable, undocumented, unclear, badly written and will have something in the middle of it that is completely unfathomable.

The programmer from twenty years ago probably thought that they were being really clever solving their problem in that particular way but that doesn’t help the programmer today, sat staring at a piece of code that read 4 bytes from a specific memory location and compares it to 4 bytes from another memory location that no longer works. Nothing else in the code references either location, what was that data doing, what is it for, where is it from, because now the ‘if’ condition comparing those two bits of data fails. All you did was recompile exactly the same code with a new compiler. Unfortunately the new compiler manages memory allocation differently. The old machine that chuntered away happily for so many years, it’s manufacturer long since out of business, doesn’t support any compiler you can find so there is no way to see what the old software was actually doing. It’s the stuff of nightmares but I’ve experienced that exact scenario myself, all because the programmer was being clever.

Code Maintainability means thinking about the person coming in behind you, needing to change your code without you being there to answer questions.

First of all, make the code obvious (most programmers only look at documentation as a last resort because it’s probably out of date). If you have to do something not obvious, write some comments in the code explaining why. Write test code and again, be obvious. Those test cases should be good enough to pick up anything the code should be doing but isn’t, and anything it shouldn’t be doing but is. The point here is that if someone has to change that code, those test cases should give them the confidence to know that the tests will tell them if they inadvertently break some functionality.

The key for these tests is functionality, they should not be testing implementation, they should be testing functionality only. This allows the code to be refactored without having to change tests, and the tests shouldn’t really care about implementation anyway. They should also test robustness, pass in bad data and make sure it can handle it gracefully. I once had a tech interview question that asked what test cases I would use to test a function that accepted 3 integers and drew a triangle, the three integers being the length of each side. So bad data here would be zeros, or negative numbers, or one huge number and two single digit numbers, things like that. Good test coverage gives developers the confidence to be able to get in and make any necessary changes and know any mistakes will be picked up by the tests.

The nirvana for coding is the ‘elegant solution’. Achieving it quite often requires skill and even luck, maintainability on the other hand isn’t that hard. The problem is, good maintainable code is often unnoticed, in the same way as an efficient shop assistant is unnoticed. People tend to want accolade for their work, unnoticed isn’t getting accolade, but with software, not being noticed is its own accolade. You notice bad software because it hangs or crashes or misbehaves. How many people admire the skill of the engineers that wrote the code on your mobile phone that slows the scrolling to a halt rather than it stopping dead, after you drag a page with your finger? It’s clever stuff but because it does the job so effectively it’s unnoticed.

Maintainability also allows software applications to have a longer life. Software usually has to change over time as the business requirements evolve. The less maintainable the software is, the sooner it has to be scrapped and rewritten from scratch. The properties of evolving software are described by the laws of software evolution, first proposed by Manny Lehman and Les Belady back in the seventies. In summary, they basically say that you have to keep the complexity of the software under control, what they call entropy, otherwise it grows to the point at which fixing one bug causes two more. By stopping the growth of system entropy and ultimately maintaining understandability, it will keep the risk associated with changing the code low, and engineers will continue to feel confident going in and working with it.

Static code analysis can also help with maintainability. Over the years, several people have tried to come up with a metric that defines maintainable code but none have really caught on. Probably the most commonly used metric is Cyclomatic complexity which measures the number of paths through a given function, most good static analysis tools measure this value as part of what they do, and it is a good indicator of problems ahead. Although it doesn’t give a full picture, a value of 10 typically indicates refactoring needs to be done.

Sometimes for huge code bases static analysis becomes time consuming and then doesn’t get done, this is where hot spot analysis can help. Hot spots are areas of the code that get changed frequently and are therefore more susceptible to entropy growth. any good source repository tool should be able to report on the number of changes made to each source file.

The aesthetics of the code is also an important part of maintainability. Everyone has their own construct preferences, for example,

Do you prefer

if (x == y) {
y++;
}

or

if (x == y) 
{
y++;
}

or maybe

if (x == y) { y++; }

or something else?

The compiler doesn’t care but the developer reading it usually does. Be consistent. If you are changing code written in a style you don’t like, stick to that style anyway, constantly changing style is much worse to try and work with than the ‘wrong’ style. Always work with that assumption that it won’t be you making the next change to this code. The next person may like the chosen style, even if you don’t.

Finally, always think about what would help you if you were looking at it for the first time. A useful comment, clean styling, well organised layout are all a safe direction to take, maybe there are other things I haven’t thought of, I don’t know everything.

Anyone who thinks they do needs to be avoided because they are usually dangerous.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store