Go Easy on the Maintenance Programmer

Maintenance Programmer“: This common, and underappreciated species of programmer is often heard muttering and cursing under their breath. Sometimes bald from tearing their hair out in frustration.

From time to time we all have to do it: simple changes and maintenance to code – often other people’s code. And the truth is that most code spends far more time in maintenance mode than it did in design, coding, or initial debugging. Yet maintenance programming is notoriously difficult (even if it does tend to get assigned to the most junior developers on a team). I will identify the main reason I think maintenance is difficult, and suggest one specific programming practices that can make your code easier to maintain.

Why Maintenance Programmers Go BaldA typical maintenance task is to make some tiny change in a larger existing codebase. The change might be to add a new feature (“we need to support a new kind of mortgage loan”) or it might be to fix a simple bug (“the odd days interest calculation doesn’t match what accounting is producing”). Except in very rare and fortunate cases, the maintenance programmer is not particularly familiar with the code in question. They are expected to read and understand it, make the change, whatever else happens, they must NOT introduce any new bugs, and since, after all, it’s “just a tiny change” they are expected to do this very quickly.

Of course, the change would have been tiny if it had been in the original requirements when the code was being written. And it might not be too much trouble if it was written last week by you, so you still remember the whole structure – the classes, the methods, the data structures, and how they all work together. But a maintenance programmer rarely has this luxury: she is typically given a large amount of code with which she is unfamiliar and asked to make some small change.

If the code is perfectly structured for this particular change, then it’s still not too difficult: just add a new entry to the “supported loan types” array, or modify the one-line formula where the odd days interest is calculated. The real challenge comes when the code needs to be refactored in order to support the new “tiny change” – perhaps lots of places in the code assume that that all mortgage loans have unique subtypes (and this new one doesn’t) or perhaps the odd-days interest calculation is spread out among several classes. (Both are real examples from last week’s coding.)

So the maintenance programmer needs to take a codebase she is unfamiliar with, and make a small refactoring – all while keeping foremost in mind that she must NOT risk introducing new bugs.


There are lots of little things that you can do when writing code to make things easier for the maintenance programmer. I strongly recommend writing a “method header” comment for every meaningful method (whether public or private) explaining what it does. I recommend avoiding anything that seems “clever”, unless you have a very good reason and extensive comments. I recommend spending an inordinate amount of time carefully choosing good names for your classes, methods, and variables. But these are well-known practices; for this essay I’ll try to describe one that is less well-known. I picked it up while teaching myself functional languages (like Haskell).

The programming practice I advocate is this: maximize locality of reference by eschewing side effects and variable rebindings. Which just goes to show that you can make anything sound difficult if you use enough long words.

The key is to realize that the maintenance programmer is not dumb, she is just trying to make a quick change in an unfamiliar codebase. When I make a small changes as a maintenance programmer, it would not be atypical for me to spend 3-4 hours reading through the code and 10 minutes typing out the change. That time is spent trying to locate the code that affects this feature and making sure that my change won’t break anything. If code were written in a way that made it easier to be sure whether a change will break things, that would make it much easier to maintain.

Any time that you go to modify a function that has side effects, you have to carefully read through every bit of code that touches the variables involved in that side effect in order to understand whether the code you are changing may break things. If those have side effects you may have to track that down as well. This is how it can take hours to decide whether 10 minutes of trivial code is safe to write.

For instance, here is a simple Java method that submits user orders.

public void submitOrders(List userOrders) {
    for (Order order: userOrders) {
        if (order.submit()) {
            this.totalValidOrders++;
        }
    }
}

The maintenance programmer has been asked to submit system orders as well as user orders. You might think this would be straightforward change:

public void submitOrders(List userOrders, List systemOrders) {
    for (Order order: userOrders) {
        if (order.submit()) {
            this.totalValidOrders++;
        }
    }
    for (Order order: systemOrders) {
        if (order.submit()) {
            this.totalValidOrders++;
        }
    }
}

And hey, it will only take you 10 minutes to type out this change! But it isn’t safe: that change may break things. There is a side affect on the instance variable totalValidOrders, and before you can make this change, you need to understand every use of the totalValidOrders variable. Are there places that assume that, after a call to submitOrders(), it will contain the total number of user orders? When system orders are handled, will those places expect to see system orders included? And are there other methods, like invalidateOrders(), that ALSO modify the totalValidOrders variable, and how do THOSE modifications interact? Researching this could take quite some time.

The whole exercise would be much easier for the maintenance programmer if the original designer had used a more functional style of programming without side effects. Suppose that the original looked like this:

/**
 * Submits the orders that were passed in and returns a count of the valid orders.
 */
public int submitOrders(List userOrders) {
    int validOrders = 0;
    for (Order order: userOrders) {
        if (order.submit()) {
            validOrders++;
        }
    }
    return validOrders;
}

Now the maintenance programmer has a much easier job. She does not need to investigate every use of some variable and try to determine the order in which other methods on the same object get called. Instead, she simply needs to examine the places where the submitOrders() method is invoked and figure out whether those need the total count of valid orders or just the count of valid user orders. This is a much more deterministic task, because it involves examining a known list of invocation spots, instead of requiring that one understand every use of a certain object (or at least a field of an object). The change to this:

/**
 * Submits the user and system orders that were passed in and returns a count
 * of the total valid orders.
 */
public int submitOrders(List userOrders, List systemOrders) {
    int validOrders = 0;
    for (Order order: userOrders) {
        if (order.submit()) {
            validOrders++;
        }
    }
 
 
    for (Order order: systemOrders) {
        if (order.submit()) {
            validOrders++;
        }
    }
    return validOrders;
}

…can be made with far less research.

Comments

10 Responses to “Go Easy on the Maintenance Programmer”

  1. RBeatty on 2008-07-18 10:51 am

    Michael,

    May I recommend that your future illustrations include a caption. The immediate question I had was not “What did Michael write?” but “Who the heck is that?”

    Roy

  2. james woodyatt on 2008-07-18 2:25 pm

    One proverb I learned at the knee of a chief architect who came out of the early Ingres developer pool is this: “Real programmers know there are only three numbers. Zero, One and Many. And the truly advanced programmers are never too sure about the distinction between One and Many.”

  3. Mark Lee Smith on 2008-07-18 3:32 pm

    Forgive me for saying this but have you not just replaced a binding with a behaviour? Conceptually submitOrders is now called wherever totalValidOrders was referenced. You still need to investigate these places to find out what value they expected.

    Minimizing side effects can certainly be an advantage in some situations, but this doesn’t strike me as one of them. It hardly reduces the amount of work required in this example!

    The claim that this is easier seems to hinge on the fact that callsites are better known (documented?) and hence easier to check. That sounds like it has very little to do with “the advantages of functional programming”.

    We’ll ignore the potential performance penalty that results from doing this as I doubt it’s that important here.

    Thanks for the interesting article, it was well written and entertaining, even if I don’t agree :).

  4. Tony Morris on 2008-07-18 4:22 pm

    Eschew side-effects is a great programming mantra. However, Java makes this extremely difficult. If you’re stuck on legacy Java junk, try moving to Scala. If this doesn’t suit the irrationality of the suits, there is Functional Java.

  5. mcherm on 2008-07-18 4:56 pm

    Mark:

    I had a REALLY difficult time writing this article, for two reasons. The first, was that it was difficult to come up with an example that was simple enough (short enough) to fit in the article. The second reason was that when I really dug into it, I discovered that I didn’t have a well-defined idea of just what I meant by code with “no side effects”.

    I am now in the Justice Stewart state with regard to this: I know it when I see it. A simple setter method isn’t “side effects” (or at least not the kind that bothers me). A method called calculateRate() that updates the rate on an object is OK (but closer to the borderline). A method that loops through a list calling a function, but also caches some kind of total in an instance variable, that IS a “side effect”.

    Certainly the second approach (without the instance variable) is DIFFERENT — the rest of the code would need to be different also. My claim is that IF the entire codebase were written this way, that it would be easier to maintain because the maintainer would have to “fully understand” a much smaller portion of the code in order to safely make the change.

  6. Mark Lee Smith on 2008-07-18 7:47 pm

    You’re not wrong: reducing side-effects can be said to increase cohesion in the system. That said I think it’s more important to strike a balance.

    There are times when side-effects make life a whole lot easier. At the same time there are situations where side-effects can cause a whole lot of trouble.

    As a general rule: eschew side-effects that you can do without, but don’t make things harder yourself by trying to make them easier.

    The functional-programming ideology is very tantalizing, but it’s easily be taken to far. Not that I’m saying you’re guilty of this :).

    I’ve done this in the past and it’s certainly not fun. I wouldn’t advocate that any programmer attempt to avoid side-effects completely. Not even Haskell programmers do that! Instead, they use handy dandy tricks like monads to keep them in check.

  7. Mark Lee Smith on 2008-07-18 7:48 pm

    Note: I’m not implying that this is what you were advocating.

  8. SeanJA on 2008-07-19 8:06 pm

    Wouldn’t adding another List to the parameters mean that you now would have to go through each line of code to make sure that they are all calling the function correctly? I know this is simple with “Right Click -> Refactor Code”, so if you were to go this route would there not be a better way of doing it? Perhaps not by sending another list through but rather having a switch type variable “int listType” perhaps?

  9. anonymous on 2008-07-21 8:33 am

    >>> This is how it can take hours to decide whether 10 minutes of trivial code is safe to write.

    Precisely.. In one of my previous jobs, I was a junior maintenance developer. I had a project manager, who asked me to change something in the morning, and I completed it in the evening. He shouted at me asking why I took a day to modify not more than 20 lines of code.

    I told him the story about an engineer who is asked to fix a machine in a factory. He spends a few hours investigating the machinery and marks the defective part with a chalk, asks the company to replace it, and sends a bill of 100. The company asks why 100 for a chalk mark. The engineer sends another bill – 1 for the chalk and 99 for knowing where to put the mark.

    I guess he got the idea.. though I had to leave the company.

  10. mcherm on 2008-07-31 8:19 am

    @Roy:

    I actually don’t KNOW “Who the heck is that?” I have an interesting practice with my illustrations. There are a few that I photograph, create or draw myself, but these are the minority. Most are pictures gathered from flickr, which people have chosen to license under a Creative Commons license that allows others to use it. You can identify these because clicking on the picture leads you to the original photographer’s site on flickr. In this case, I searched for a good photograph of someone pulling their hair out (preferably a woman since on my blog the “default” programmer is usually a woman). As it turns out, someone whose username is “dearbarbie” took an excellent picture of just that.

Leave a Reply