Traditional metrics for the development of a software project watch cost, defect rates, and schedule. All are measured, of course, against the project scope document created at the start of the project or at least measured against the expectation that was set when the project was started. While there are still far too many software development projects that don't meet these metrics there are other less visible factors to the long-term success of the project.
We are all aware that most of the time and effort spent on a software development effort is not spent within the design and construction phases. Most of the time and effort is spent supporting, maintaining, and enhancing the system. Subtle differences in the code can make these parts of the software lifecycle painful or relatively pain free.
The problem with software is that some systems are infinitely more difficult to maintain than others. The additional difficulty in maintaining the software far outstrips any benefit that is gained by negotiating with vendors, maintaining tight schedules, or any of the other project management techniques that are typically used to control project costs. The testing process, which removes bugs may not necessarily improve the ability to find bugs, accept configuration changes, or make enhancements. The reason for the difficulty in maintaining systems can be broken down into three key areas.
System maintainability starts with the ability to understand the existing system and part of understanding the system is a proper set of documentation. Unfortunately, too many systems are delivered with missing or inadequate documentation. Often this is because developers don't know the kind of documentation necessary to maintain a system. They develop documentation that is of little or no value.
Every system should have an architectural document that describes all of the major components, how they fit together, and how they communicate between each other. This helps to form the framework for understanding the application. When this document is missing it means that understanding how the entire system fits together will be difficult and therefore systemic problems may difficult to find. This is the document that helps you understand the forest before you go hunting for a tree.
The next piece of documentation isn't a separate document at all. It's meaningful comments in the source code. The comments that should be in the code include the header comments that describe each function, it's parameters and returns, and comments which describe why the code is being used.
Everyone has seen comments above functions, which describe what they do and what the parameters are. These are some of the easiest comments for the developer to generate. They need only cover the standard pattern of name, usage, parameters, and return.
The more difficult, but necessary, type of comments are the ones that explain why not what the code does. The extreme example of this is a line that subtracts one from a variable. A bad comment is the one that say 'Removes one from the variable.' Anyone who can read the syntax of the code knows that.
The good comment is the one that explains why one must be removed from the number. For instance, 'Convert from base 1 to base 0.' That comment explains not what is being done but why it's necessary.
No amount of documentation can overcome a bad architecture. The architecture of the application, no matter what the language, has the most profound impact on the ultimate maintainability of the application. Applications with well thought out, flexible, and structured architectures are the easiest to maintain because they are the easiest to understand and the easiest to extend.
Most architectural problems are caused by lack of forethought. Whether the development project was started with no design phase or whether it was a design phase that was excessively constrained, the result is the same. An architecture, which cannot be expanded to support potential new needs, is difficult to maintain because the architecture itself may need to be changed during the maintenance phase. In some cases this can be likened to pouring the foundation after the building has been built. While it's technical possible, it's never the easiest way.
It's also painful from a psychological standpoint that a project that has just recently been completed must be reworked to support what are perceived to be minor changes. The barrier that exists within corporate management is that they felt like they were done with the large expenditures on the project. Selling why it is necessary to re-architect a part or all of an application is a very difficult sale.
Error handling and logging
The final area that will determine how difficult or easy a project is to maintain is the error handling and error logging that occurs. When a problem is found the most intensive and time consuming process is determining what the root cause is. This typically takes substantially longer than the process of fixing the problem itself.
Generally problems are simple to solve once the problem is fully understood. Identifying exactly where the error is caused is the key in understanding the problem. Applications that are designed from the start to identify the exact cause of the error are much more likely to be maintainable than software that was not built with this mandate.
One of the things that is most often added to code after it's been delivered is better error handling and logging facilities so that problems can be resolved quicker and easier. Take Microsoft Word for instance. Eight versions of the product came out each one with, arguably, better error handling. Version 9, Word XP, now allows you to send an automatic bug report to Microsoft for analysis. This is a much higher level of error logging that takes advantage of the logging technologies available today.
Fragile code tends not to have good error handling or logging. The tendency is for the application to receive a general protection fault and have the operating system shut the application down. The problem with this is that this doesn't help identifying the cause of the problem. Without modification to add or improve error handling and logging it may be almost impossible to make progress on problem resolution.
If you've inherited some code that is fragile you'll need a set of coping skills that will allow you to harden the code that you have even if you can't totally re-architect it. The best place to start is in shoring up the error handling, logging, and adding the ability to trace the application.