Corporate developers spend majority of their programming time doing maintenance work. My basis for this claim is two years worth of statistics that I have been gathering at my workplace. According to these figures, my group spends about 65 percent of their programming time on maintenance (with some developers spending considerably more, depending on the applications they support). I suspect these numbers are applicable to most corporate IT shops – and possibly, to a somewhat smaller extent, to software houses as well. Unfortunately, maintenance work is often looked upon as being “inferior to” development. This being the case, it is worth dispelling some myths about maintenance programming. As it happens, I’ve just finished reading Robert Glass‘ wonderful book, Facts and Fallacies of Software Engineering, in which he presents some interesting facts about software maintenance (among lots of other interesting facts). This post looks at these facts which, I think, some readers may find surprising.
Let’s get right to it. Fact 41 in the book reads:
Maintenance typically consumes 40 to 80 percent (average 60 percent) of software costs. Therefore, it is probably the most important life cycle phase of software.
Surprised? Wait, there’s more: Fact 42 reads:
Enhancement is responsible for roughly 60 percent of software maintenance costs. Error correction is roughly 17 percent. Therefore software maintenance is largely about adding new capability to old software, not fixing it.
As a corollary to Fact 42, Glass unveils Fact 43, which simply states that:
Maintenance is a solution, not a problem.
Developers who haven’t done any maintenance work may be surprised by these facts. Most corporate IT developers have done considerable maintenance time; so no one in my mob was surprised when I mentioned these during a coffee break conversation. Based on the number quoted in the first paragraph (65 percent maintenance) and Glass’s figure (60 percent of maintenance is modification work), my colleagues spend close to 40 percent of their time of enhancing existing applications. All of them reckon this number is about right, and their thinking is supported by my data.
A few weeks ago, I wrote a piece entitled the legacy of legacy software in which I pointed out that legacy code is a problem for historians and programmers alike. Both have to understand legacy code, albeit in different ways. The historian needs to understand how it developed over the years so that he can understand its history; why it is the way it is and what made it so. The programmer has a more pragmatic interest – she needs to understand how it works so that she can modify it. Now, Glass’ Fact 42 tells us that much of maintenance work is adding new functionality. New functionality implies new code, or at least substantial modifications of existing code. Software is therefore a palimpsest – written once, and then overwritten again and again.
The maintenance programmer whose job it is to modify legacy code has to first understand it. Like a historian or archaeologist decoding a palimpsest, she has to sort through layers of modifications made by different people at different times for different reasons. The task is often made harder by the fact that modifications are often under-documented (if not undocumented). In Fact 44 of the book, Glass states that this effort of understanding code – an effort that he calls undesign – makes up about 30 percent of the total time spent in maintenance. It is therefore the most significant maintenance activity.
But that’s not all. After completing “undesign” the maintenance programmer has to design the enhancement within the context of the existing code – design under constraints, so to speak. There are at least a couple of reasons why this is hard. First, as Brooks tells us in No Silver Bullet — design itself is hard work; it is one of the essential difficulties of software engineering. Second, the original design is created with a specific understanding of requirements. By the time modifications come around, the requirements may have changed substantially. These new requirements may conflict with the original design. If so, the maintenance task becomes that much harder.
Ideally, existing design documentation should ease the burden on the maintenance programmer. However it rarely does because such documentation is typically created in the design phase – and rarely modified to reflect design changes as the product is built. As a consequence, most design documentation is hopelessly out of date by the time the original product is released into production. To quote from the book:
Common sense would tell you that the design documentation, produced as the product is being built, would be an important basis for those undesign tasks. But common sense, in this case, would be wrong. As the product is built, the as-built program veers more and more away from the original design specifications. Ongoing maintenance drives the specs and product even further apart. The fact of the matter is, design documentation is almost completely untrustworthy when it comes to maintaining a software product. The result is, almost all of that undesign work involves reading of code (which is invariably up to date) and ignoring the documentation (which commonly is not).
So, one of the main reasons maintenance work is hard is that the programmer has to expend considerable effort in decoding someone else’s code (some might argue that this is the most time consuming part of undesign). Programmers know that it is hard to infer what a program does by reading it, so the word “code” in the previous sentence could well be used in the sense of code as an obfuscated or encrypted message. As Charles Simonyi said in response to an Edge question:
Programmers using today’s paradigm start from a problem statement, for example that a Boeing 767 requires a pilot, a copilot, and seven cabin crew with various certification requirements for each—and combine this with their knowledge of computer science and software engineering—that is how this rule can be encoded in computer language and turned into an algorithm. This act of combining is the programming process, the result of which is called the source code. Now, programming is well known to be a difficult-to-invert function, perhaps not to cryptography’s standards, but one can joke about the possibility of the airline being able to keep their proprietary scheduling rules secret by publishing the source code for the implementation since no one could figure out what the rules were—or really whether the code had to do with scheduling or spare parts inventory—by studying the source code, it can be that obscure.
Glass offers up one final maintenance-related fact in his book (Fact 45):
Better software engineering leads to more maintenance, not less.
Huh? How’s that possible.
The answer is actually implicit in the previous facts and Simonyi’s observation: in the absence of documentation, the ease with which modifications can be made is directly related to the ease with which the code can be understood. Well designed systems are easier to understand, and hence can be modified more quickly. So, in a given time interval, a well designed system will have more modifications done to it than one that is not so well designed. Glass mentions that this is an interesting manifestation of Fact 43: Maintenance as a solution, rather than a problem.
Towards the end of the book, Glass presents the following fallacy regarding maintenance:
The way to predict future maintenance costs and to make product replacement decisions is to look at past cost data.
The reason that prediction based on past data doesn’t work is that a plot of maintenance costs vs. time plot has a bathtub shape. Initially, when a product is just released, there is considerable maintenance work (error fixing and enhancements) done on it. This decreases in time, until it plateaus out. This is the “stable” region corresponding to the period when the product is being used with relatively few modifications or error fixes. Finally, towards the end of the product’s useful life, enhancements and error fixes become more expensive as technology moves on and/or the product begins to push the limits of its design. At this point costs increase again, often quite steeply. The point Glass makes is that, in general, one does not know where the product is on this bathtub curve. Hence, using past data to make predictions is fraught with risk – especially if one is near an inflection point, where the shape of the curve is changing.So what’s the solution? Glass suggests asking customer about their expectations regarding the future of the product, rather than trying to extrapolate from past data.
Finally, Glass has this to say about replacing software:
Most companies find that retiring an existing software product is nearly impossible. To build a replacement requires a source of the requirements that match the current version of the product, and those requirements probably don’t exist anywhere. They’re not in the documentation because it wasn’t kept up to date. They’re not to be found from the original customers or users or developers because those folks are long gone…They may be discernable form reverse engineering the existing product, but that’s an error-prone and undesirable task that hardly anyone wants to tackle. To paraphrase an old saying, “Old software never dies, it just tends to fade away.”
And it’s the maintenance programmer who extends its life, often way beyond original design and intent. So, maintenance matters because it adds complexity to the legacy of legacy software. But above all it matters because it is a solution, not a problem.