Software support: maintenance, reverse engineering and evolution
◈ 4 cardsThe four maintenance types and why the distribution matters for budgeting; reverse engineering as design recovery rather than decompilation; refactoring that changes structure but not behaviour; and the software evolution cycle.
The thinking move: "maintenance" is a budget line, and mis-naming it mis-funds it
Ask a manager what maintenance costs and they will price bug-fixing. That estimate is wrong by a large factor, and the reason is a vocabulary failure rather than an arithmetic one. Support work divides into four types with different causes, different triggers and very different volumes, and only one of them is bug-fixing. Getting the four straight is a cheap definition question and a genuinely useful professional habit.
The four types
Corrective maintenance removes latent defects: the software does not do what it was specified to do, and you fix it. Adaptive maintenance responds to a changed environment — a new operating system version, a new device, a new regulation, a payment provider's API deprecation — with no change in function from the user's point of view. Perfective maintenance, also called enhancement, adds or improves function at the user's request; this is where new functionality genuinely enters an existing product. Preventive maintenance, also called re-engineering, restructures the software so that future change is safer and cheaper, with no visible change at all. Corrective and adaptive add no new functionality; perfective certainly does, and preventive occasionally does as a side effect. Perfective and preventive are the two proactive types — they are chosen, not forced.
The distribution is the insight. Enhancement and adaptation dominate real support budgets, so treating support as a bug-fixing line item under-funds it badly and, worse, leaves no budget at all for the preventive work that would reduce the other three.
Understanding what you are about to change
Before any maintenance request can be answered, the engineer has to understand a system that is usually under-documented — the accumulated technical debt of features added without documentation or impact analysis. Reverse engineering is the answer: analysing an existing system to identify its components and their interrelationships and to create representations of it in another form or at a higher level of abstraction. It is design recovery, not decompilation, and it operates at several levels — recovering the data (internal structures, global data, sometimes an entire class model), the processing, and the user interface. There is no magic slot: what comes out depends on the source, the tooling and the analyst.
Refactoring is the process of changing a software system so that it does not alter external behaviour but does improve internal structure. It applies at the level of code and local data structures; when the restructuring reaches past module boundaries into the architecture it has become forward engineering, not refactoring.
Software evolution, or re-engineering, is the larger cyclical process for taking an existing system and generating a new one of the quality modern practice would produce. Its activities are inventory analysis, document restructuring, reverse engineering, code refactoring, data refactoring and forward engineering — and because it is a cycle, a project may stop after any one of them. Where resources are short, the Pareto move is to apply it to the twenty percent of the software that causes eighty percent of the problems.
Worked example — four requests in one BorrowBox week
Four tickets arrive on the same Monday, and naming them correctly changes who pays and when they are scheduled.
- Members are occasionally charged a late fee twice when a return scan is retried. The specified behaviour is one charge. Corrective — unplanned, urgent, budgeted as defect work.
- Android's next release tightens background execution, and the app's overnight reservation-expiry job will stop running. Nothing the member sees changes; the environment did. Adaptive — forced on us by an external calendar, and precisely the type that a bug-fixing budget never anticipates.
- The librarian asks for a waitlist so that members can queue for a tool that is out. New function, user-requested. Perfective — this is a small project, and should be planned as one.
- The late-fee logic has grown to a cyclomatic complexity of 22 and regressed in each of the last two releases. Extracting a
FeePolicycomponent changes nothing a member can observe. Preventive — and it is the only one of the four that will reduce the cost of the other three next year, which is why it is also the first to be cut and the one that must be argued for explicitly.
Before doing (4) the team reverse-engineers the current fee rules out of the code, because the written rules are three years stale — design recovery on their own product, which is the normal case rather than an exotic one.