Long ago, I read an article that claimed all development was maintenance because a code base was created the moment a line of code was written. I agreed with the conclusion, but not with the argument. I believed maintenance was a type of interaction with existing code.
Later, I was re-reading about refactoring and realized how it blurred the line between development and maintenance.
Even later, I needed to port a legacy system. Originally I planned a rewrite due to issues with the code base, but chose a refactoring based port instead. The results showed the existing code base was an asset, not a liability. I then realized my bias against legacy systems clouded my judgment and almost led me to a costly rewrite.
All these changed my views on development. In this article, I will argue and explore the claim that all development – including new development – is maintenance.
The Centrality of Maintenance
First, outside of working, the most important features of good code are non-functional things like the right architecture, names, short methods and so on. These are for people, and the people who care are maintainers. Therefore, new development practices are a function of (expected) maintenance.
Second, maintenance is done even during initial development. For instance, during development, developers may revisit existing code to fix bugs, alter behavior, or even to complete an incomplete section (due to interruptions, etc…).
Third, maintenance is the longest part of the software’s life. It can last for DECADES and is the bulk of software’s cost.
With maintenance central to development, it should be stressed, yet the opposite is true. Even worse, many misconceptions surround maintenance.
Misconceptions Regarding Maintenance
Some think maintenance starts when software is “finished”. In truth, software is never finished. Rather, initial development gives us version 1 and maintenance gives us the remaining versions until the code is retired. When you upgrade your software, you are likely getting the results of maintenance and not a rewrite. In short, maintenance is the ongoing development of an existing system.
Some think maintenance means fixing bugs. In truth, most maintenance is enhancement.. New features are added and — if refactoring is used – even the design can be changed.
Some think maintenance is a problem,. In truth, it’s a sign of success. It means the software is useful enough to get enhanced.
Some think maintenance is costly. In truth, not only is it cheap, it’s often a cash cow. It only seems costly because people look at the total cost over the software’s life. Per release, maintenance is much cheaper than new development (since most of the functionality exists, and we’re just enhancing it), which means the profit margins on sales (and possibly upgrades) can be much higher than in version 1.
Some think maintenance is a software phase. In truth, it can happen any time. Whenever we modify existing code, that’s maintenance. For example, re-using components in an existing system is not maintenance, but can be done during the “maintenance phase”. Rewriting a method is maintenance, and can be done during the “development phase”.
If we don’t understand the biggest driving force of development, how can we competently practice, or improve our practice?
What can be done?
First, CS education should focus on maintenance. Students should learn techniques for code comprehension, analyzing dependencies and building semantic models of program execution. The concept of formal systems should be extended to include existing code, and students should learn to reason in the context of it, even if the code is poor or counter-intuitive.
Second, companies should erase the development/maintenance split and evaluate everything in terms of maintenance. Adopt tools only if they ease maintenance. Use code reviews to ensure maintainable code. Reward things like simple, clear, self-documenting code that is tightly focused to its purpose. Penalize things like “clever” code, over-use of comments, “featuritis” and premature optimizations.
Third, replace intuition with discipline. During coding we have a detailed understanding of the code, and hence cannot be trusted to sympathize with maintainers who won’t have this understanding. Therefore, we need to follow practices, even if they seem pointless at the time.
Not So Obvious
Look at readability. It seems intuitive, and the rules are clear: use descriptive names. But there’s more to it than that. For example, what is more readable, line 1 or 2?
- primaryBuffer[i] = secondaryBuffer[i]
- primaryBuffer[bufferPosition] = secondaryBuffer[bufferPosition]
I say line 1, even though i is a “less descriptive” name than bufferPosition. i has a meaning by convention, is tightly associated with loops and the structure of the code shows through with the shorter name. Generally speaking, i,j,k are loop indexes corresponding to nesting, so they carry a great deal of semantic information, including the code structure.
There is a lot more to variable names than length. There’s history, culture, and the aspect of the code we are trying to emphasize. Long names emphasize the entities while short names emphasize the relationships. What are we trying to emphasize? It can differ from case to case in the same body of code.
For the opposite case, what about: someItem or m_someItem for a member variable? someItem looks cleaner, and while coding we may prefer that because it flows and the code looks more readable. In that case, we’d be confusing aesthetics with readability. m_someItem is better because it carries valuable information about the scope of the variable, which a maintainer needs to know. Of course if we’re using a language that forces a this/self reference with members, this is moot.
If prefixing can carry valuable information, what about the (now widely ridiculed) Hungarian notation, wherein the type of the variable is part of the prefix? Do maintainers need to know types? What does this do to data abstraction? If data types change, we have to rename entities. Or take what the inventor really mean — Hungarian notation to reveal ROLE and not type. How does that square with readability?
Other than showing how little we know about something as “simple” as readability, this raises a question: what IS readability? We often think of it like reading a book, but do developers read source like prose, or do they skim it like a tax form, usually to find out the impact of a potential change? Understanding why we read can dramatically impact coding style. This means many readability conventions are wrong, and entire practices like Literate Programming are a mistake that reveal our deep-seated ignorance of this invaluable topic. But then, we were never trained in reading source; we were told just to do it as if it were no different from an article or book.
A New Metaphor
How can we think of development in a way that eliminates the development/maintenance dichotomy? We can use a factory metaphor.
A software project is like a factory. Version 1 produces the factory and the first product run. The factory’s machines are designed to support multiple product lines and the factory is designed to be as easy to refit as possible.
The factory cost a lot, so it should run a long time. Therefore, the people hired to produce the products not only can build and refit machines, but are trained to make the most use of the existing machines.
Efficiency is constantly monitored to ensure products are good, refits are minimal and costs are low. In addition, product lines are monitored for trends we can anticipate with refits.
Sometimes machines run poorly due to accumulated refits, and they may need redesigning. If it improves profitability, they are redesigned.
Eventually, we may realize a new factory will be more profitable. We then build a new factory, using everything we learned about building and operating the old factory to make something even better that can run for ages.
Of course all decisions are based on hard numbers and not guess-work or intuition.
Towards a Discipline
A maintenance discipline is beyond the scope of this article, but to hint at what is involved, I want to explore dependencies. Dependencies can be a big source of maintenance problems, so how do we reduce dependency-related issues?
First, design plug-ins so maintainers can extend the system without touching existing code. Plug-ins could include reports and pre & post processing objects. A lot can be done with just those. Of course we should have evidence these plug-ins will get used.
Second, restrict mutable state since dependency issues occur because of unexpected state changes. In fact, some paradigms are about state management. For instance, Functional Programming eliminates mutable state while OOP manages it.
Third, make state changes clear. For instance, names of methods that modify an object’s state should start with Set. This way, maintainers know if there are side effects.
Fourth, clearly communicate scope. For instance, use naming conventions for member variables if the language does not enforce such naming. This way, maintainers know how far effects can ripple.
The Elephant in the Room
I skipped one obvious thing: many developers hate maintenance. How do I respond to that?
Some of the hatred results from developers’ dislike of a task they think unpleasant, taxing and menial. Improving the science of maintenance will improve the maintenance experience both by training people to be more efficient maintainers, and training people to develop maintainable systems, using the knowledge of said training.
Some hate it because they often follow someone’s vision rather than creating their own. However, maintenance can be creative. Code comprehension can mean building semantic models, call charts, and exercising formalisms – tasks developers regard as creative. Besides, we are always following the visions of others, even in initial development. Libraries, VMs, frameworks and coding standards are imposed on us and constrain how we work. Besides, initial development involves tons of distracting detail that is often absent from maintenance. Here’s an example.
You want to process some data. If this is a new project, you have to deal with the file system, GUI, error handling, settings, exports, etc… none of which are your actual task. In fact, you will usually spend much more time on these than your real task! However, if you are working in an existing system, you may simply be able to create an object with input and output parameters, and program your projections as a mapping from input to output, since the system’s policies handle all the rest. Now your focus is on the task at hand, the domain. Of course, this assumes a properly designed system…
Let’s face it, if you don’t like working with existing code, you will dislike maintenance, period. You can try to change your perspective, find aspects of it that speak to your talents and likes, but ultimately you have to accept it. As for debugging, I can’t imagine it ever being enjoyable. Fortunately, a science of maintenance should reduce the need and extent of debugging by making the system more obvious so that fewer errors are introduced and existing errors are found more quickly.