Memra

Software support: maintenance, reverse engineering and evolution

◈ 4 cards

The 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.

  1. 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.
  2. 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.
  3. 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.
  4. The late-fee logic has grown to a cyclomatic complexity of 22 and regressed in each of the last two releases. Extracting a FeePolicy component 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.

TypeTriggerNew function?BorrowBox ticketCorrectivea latent defectnodouble-charged latefeeAdaptiveenvironment changednoAndroid backgroundlimitsPerfectiveuser requestyesadd a tool waitlistPreventivefuture change risknoextract FeePolicyPerfective and preventive are proactive.
The four tickets that arrived on one BorrowBox Monday. Read the "visible?" column across: only perfective changes what a member experiences, which is exactly why the other three are under-budgeted and preventive is the first work to be cancelled.
Inventory analysiswhat is worth savingDocument restructuringmake it readableReverse engineeringrecover the designCode refactoringstructure, not behaviourData refactoringthe harder halfForward engineeringrebuild to modern practiceStop after any activity.
The evolution cycle applied to the BorrowBox fee component. It is cyclical and a project may stop after any activity — the team here stopped after code refactoring, having decided the data model did not need rebuilding.
NORMAL ~/memra/learn/comp-410/software-support-and-maintenance-types utf-8 LF