Maintainable Code

How do you define maintainability, in terms of software?

I've watched many an interaction where one group lists "maintainable" as a requirement, another team implements code that they feel is perfectly within requirements, and the original group doesn't feel the requirement was nearly met. It's an objective term in the past tense - the code was maintained - but a subjective term in the present. Does it - should it - involve unit testing, for example? One group might say those are two separate areas, and another might sensibly say that changing (maintaining) the code without unit testing is walking a tightrope without a net.

Here's my take on it. Maintainable code is, by default, code that was designed to be maintained. Maintenance might be fixing bugs, changing existing code, and adding new code; past, present, and future. It might also need to be done by a completely new set of developers; the original developer might be "hit by a bus", or, more positively, "meet the love of his life, get married in Vegas, and move next week to Patagonia to rent climbing equipment to Californians". So, what's that mean?

  • Maintainable code is self-documenting, commented, and/or externally documented in such a way that a new developer can understand what's going on without help. If there's an intuitive way and a quick way, it's done in the intuitive way. The original developer should be able to come back to their code *years* later, and pick it up again without much difficulty. Switching developers happens.
  • Maintainable code is easy to troubleshoot. If a problem happens, the code is well logged to find out where it broke down, and as easy as possible to trace through afterwards. File, function, and variable names all make sense, so a developer knows what *should* be happening there even if it isn't. Bugs happen.
  • Maintainable code is easy to change. If you want to update your user interface but keep everything else the same, it shouldn't be unnecessarily difficult. A separation of concerns comes in handy here, and only having one point of contact for each layer of your system. Changes happen.
  • Maintainable code is architected in an extensible way. If you want to add a module to the system, or add another user access point, it shouldn't be a disaster in the making. Additions happen.

There are a few exceptions, and when you find them, they stick out like sore thumbs. Business logic, for example, isn't always intuitive; if it isn't, you're going to need more documentation or comments, enough to explain both why and how it's doing what it's doing. Business logic should also sit apart from the application logic; what gets you from page to page or form to form *isn't* the same place code goes to massage data from an external XML source into a form, for example.

Am I missing something? Too narrow, still? Too broad?


mick129 said...

I just took a software testing class which didn't have much practical use (more focus on organizational structures which are necessary to support efficient testing), but it did address maintainability. The only objective measure mentioned was cyclomatic complexity. Basically, keep nested conditions and compound and negative logic to a minimum.

Dean Jackson said...

We've had good luck with the Command design pattern to split up enormous if/ifelse statements that could be represented as a switch(). Loading each choice into a Map<String, Class>, having classes implement an interface with public void execute(), and then branching on String.

At first, that seemed a lot of overhead. In retrospect, we have a very small area in the class that does the logic and branching, and the actual heavy lifting of the code happens in nicely segmented blocks.

Slow, slow progress.