
Many application failures look sudden only from the user's point of view.
A checkout page becomes painfully slow on Friday afternoon, but the database query behind it has been getting slower for months. An authentication flow stops working after a platform change, even though the deprecation notice arrived two releases earlier. A production service runs out of disk space overnight, while storage consumption has been climbing steadily enough that the failure could have been predicted.
By the time a user notices the problem, the application may have been giving off warning signs for weeks.
Preventive app maintenance is the work of identifying those signs and correcting credible weaknesses before they turn into user-facing failures.
It is different from corrective maintenance, where the defect has already appeared and the team needs to repair it. Preventive work starts earlier: an application may still be functioning normally, but monitoring, code quality, dependency status, performance trends or operational experience suggests that leaving something untouched will create unnecessary risk.
The difficulty is not understanding why prevention sounds useful. The difficult part is deciding what is actually worth fixing before it breaks.
Done badly, preventive maintenance becomes speculative engineering: rewriting stable code because it looks old, upgrading technology without a clear reason or spending weeks preparing for failure scenarios that have little business consequence.
Done well, it focuses engineering effort on weaknesses for which there is evidence.
[Internal Link: “The 4 Types of Software Maintenance: Corrective, Adaptive, Perfective and Preventive”]
Teams rarely receive an alert saying:
This component will become a production incident in 47 days.
Instead, preventive maintenance depends on interpreting smaller signals.
Perhaps memory usage has risen consistently across the last four releases. A dependency now has a known vulnerability, although the vulnerable code path has not been exploited. A module has caused several near-regressions because engineers struggle to understand it. Database storage will reach its current limit within the quarter if growth continues at the present rate.
None of these conditions necessarily means the application is failing today.
They indicate direction.
Google's Site Reliability Engineering guidance describes monitoring not only as a way to detect current failures, but also as a way to analyse long-term trends and identify conditions where something may break soon. Its four commonly referenced signals—latency, traffic, errors and saturation—are useful partly because changes in those signals can reveal deterioration before a full outage occurs. Google SRE: Monitoring Distributed Systems
That distinction is central to preventive maintenance.
Corrective maintenance asks, “What failed?”
Preventive maintenance asks, “What evidence suggests we are moving toward failure?”
Many teams configure alerts around hard limits: CPU above a threshold, storage nearly full, error rate above a certain percentage.
Those alerts matter, but preventive maintenance often begins before the threshold is crossed.
Suppose an API's typical response time has increased from 300 ms to 350 ms, then 420 ms, then 510 ms across several releases. None of those numbers may trigger an incident alert, yet the trend deserves investigation.
The underlying cause could be growing database tables, an additional network request, increased payload size or a change in user behaviour. If the pattern continues unchecked, users eventually experience what looks like a sudden performance problem.
Apple's performance guidance explicitly recommends a continuous cycle of gathering information, measuring behaviour, making a targeted improvement and checking the result. Importantly, Apple also recommends preventive performance work even when measurements do not yet show an urgent problem, because the goal is to stop performance from regressing over time. Apple: Improving Your App's Performance
For product and engineering teams, this means maintenance reviews should include questions such as:
An application can remain inside every formal threshold while its health is moving steadily in the wrong direction.
[Internal Link: “How to Detect App Problems Before Your Users Report Them”]
Dependencies are one of the clearest examples of preventive maintenance because they can become risky while the application continues to work normally.
A framework version reaches the end of its support period. A package receives a security advisory. An external library becomes effectively unmaintained. The application may continue running without any immediate user-facing symptoms, but the cost and risk of leaving that component untouched have changed.
The OWASP Top 10:2025 category on Software Supply Chain Failures specifically identifies risks associated with unsupported or outdated software, unmaintained third-party components and dependencies with known vulnerabilities. OWASP recommends tracking component versions and regularly reviewing vulnerabilities rather than waiting until a component creates an incident. OWASP Top 10:2025 — Software Supply Chain Failures
Preventive maintenance does not mean updating every dependency whenever a new release appears. That creates a different kind of instability.
The useful questions are more specific:
Is the version still supported?
Does the update address a relevant vulnerability or compatibility issue?
How far behind are we becoming?
Will delaying this update make the eventual migration materially harder?
A one-version upgrade may be routine today and a multi-version migration two years later. Preventive work tries to keep that distance manageable.
Security patching follows the same principle. The NIST Secure Software Development Framework recommends not only finding and addressing vulnerabilities in released software, but also understanding their root causes so similar weaknesses are less likely to recur. NIST SP 800-218: Secure Software Development Framework
That last part is important. Preventive maintenance is not only about installing a patch; sometimes the better preventive action is changing the process or technical condition that allowed the same class of weakness to appear repeatedly.
[Internal Link: “Why Security Patching Should Be Part of Every App Maintenance Plan”]
Some of the strongest preventive signals come from engineers rather than dashboards.
A developer opens a module and warns that even a small change could affect half the application. Tests around one component fail intermittently, so the team routinely reruns them until they pass. A deployment procedure requires several manual steps that only one engineer fully understands.
Nothing has necessarily failed yet.
But the probability of future failure has increased.
This is where targeted refactoring becomes preventive maintenance. Martin Fowler defines refactoring as changing the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour. Martin Fowler: Definition of Refactoring
The phrase without changing its observable behaviour explains why users may never know the preventive work happened. A developer can split an oversized component, simplify tangled logic or improve an error-prone interface and release an application that looks exactly the same from the outside.
The value appears later, when the next change is easier to implement and less likely to introduce a defect.
Not every piece of unattractive code deserves refactoring, though. Preventive work is strongest when there is evidence of operational or development risk, such as:
These are not purely aesthetic concerns. They affect the application's ability to be Tested, Explainable & Debuggable, Version Controlled and Seamlessly Updatable.
Capacity failures are often unusually preventable because many resources grow in measurable ways.
Database storage, queue depth, API volume, concurrent users, memory consumption and processing time can all provide advance warning when demand begins approaching a system's practical limits.
Google's SRE guidance treats saturation as a measure of how close a service is to its constraints and specifically notes that teams should pay attention to predicted saturation, not merely resources that are already exhausted. It also points out that many systems begin degrading before utilisation reaches 100%.
That creates a useful window for preventive maintenance.
If a database will plausibly exhaust available storage within the next three months, the team has options. It might increase capacity, archive unnecessary data, change retention, reduce growth or redesign a particularly expensive part of the workload.
If the team waits until the database fills at 2 a.m., those options collapse into incident response.
The same logic applies to application load. Preventive maintenance does not require building infrastructure for imaginary scale, but it should connect known product growth with technical limits. If a major customer launch is expected to double usage, the maintenance conversation should happen before that traffic arrives.
An application can develop an unhealthy pattern where individual incidents are resolved efficiently but the same category of incident keeps returning.
Perhaps a service occasionally consumes too much memory and is restarted. A queue gets stuck and operations clears it. A scheduled job misses its window and someone manually reruns it.
Each intervention restores service, so the organisation feels responsive.
From a preventive-maintenance perspective, however, recurrence is evidence that the underlying condition has not been addressed.
One incident may be noise. The fifth similar incident is a pattern.
Preventive maintenance asks what could eliminate or reduce that class of failure. That might mean changing retry behaviour, improving resource handling, adding validation, replacing a fragile dependency or simply adding monitoring earlier in the chain so the team can intervene before customers are affected.
NIST's security guidance applies the same principle specifically to vulnerabilities: fixing an individual weakness is valuable, but examining its root cause can prevent similar vulnerabilities from recurring.
The idea generalises well beyond security.
Maintenance becomes preventive when lessons from small failures change the system rather than merely closing the ticket.
A defect that has already occurred gives the team valuable information: the application contains a behaviour worth protecting against.
When appropriate, the corrective fix should therefore be accompanied by a test that reproduces the original failure condition and confirms the corrected behaviour.
That transforms one incident into preventive protection against recurrence.
AWS recommends testing application code, infrastructure, configuration, security controls and operational procedures as appropriate, with automated tests used to create repeatable feedback where they make sense. AWS Well-Architected: Test and Validate Changes
The preventive value of testing is especially clear around high-risk components. If a calculation has caused several financial errors, or authentication has broken repeatedly during dependency updates, adding stronger regression coverage can be more valuable than another round of manual checking after the next change.
Tests should not be added indiscriminately. A test suite that is slow, brittle or full of irrelevant coverage can become its own maintenance burden.
The question should be:
What failure would be expensive enough that we want an automated warning if someone accidentally reintroduces it?
Another common source of preventable failure is an external provider announcing a future change that nobody acts on.
An API version will be retired in six months. An operating system deprecates a capability the application uses. A database provider announces that an older engine version will reach end-of-support.
The application often keeps working right up until the deadline, which makes the maintenance work easy to postpone.
This is exactly why it belongs in preventive maintenance.
Teams should keep a small inventory of critical external dependencies and know who receives technical notices from those providers. A deprecation announcement should become a planned maintenance item with enough time for analysis, implementation and testing rather than an emergency ticket near the shutdown date.
That keeps the application Current & Updated, Compatible and Integrable without requiring unnecessary change for its own sake.
Code is not the only thing that can fail.
A recovery procedure nobody has tested can fail when it is needed. A manual deployment process can produce inconsistent results. An incident playbook may describe an architecture that changed a year ago.
AWS's Well-Architected guidance recommends anticipating failure, testing failure scenarios and regularly refining operational procedures. It also encourages frequent, small and reversible changes because limiting the scope of a change makes troubleshooting and recovery easier. AWS Well-Architected: Operational Excellence
Preventive maintenance should therefore ask whether the organisation itself can respond effectively when something goes wrong.
That can mean testing a restore procedure, checking that rollback still works, updating an incident runbook or removing an outdated manual step before it causes confusion during a real failure.
This is preventive maintenance in a very literal sense: the application may be healthy, while the process required to keep it healthy is beginning to decay.
This is the central judgement in preventive maintenance.
Every codebase contains things that could theoretically be improved. Engineering time is finite, so “this might cause a problem one day” is not enough.
A useful preventive-maintenance decision can be made by considering four factors:
Question
Why it matters
How strong is the evidence?
A measurable trend or recurring near-miss deserves more weight than a vague concern.
What happens if we do nothing?
Consider user impact, security, data, revenue and operational consequences.
How likely is the problem to materialise?
A supported dependency is different from one reaching end-of-life next month.
Is prevention cheaper or safer now?
Early action is most valuable when delay materially increases complexity or risk.
Consider two pieces of old code.
The first has changed once in five years, has good tests and causes no operational issues. Rewriting it because it looks unfashionable is difficult to justify as preventive maintenance.
The second is modified every month, has repeatedly caused regressions and depends on an unsupported library. Improving it has a much clearer preventive case.
The difference is evidence.
Overenthusiastic preventive maintenance can be almost as damaging as neglect.
Teams can spend months modernising stable components while high-value product work waits. They can replace technology because something newer exists rather than because the current choice creates meaningful risk. Large “preventive” rewrites can even introduce more defects than the old system contained.
Good preventive maintenance is deliberately selective.
Before approving the work, ask:
What specific failure or maintenance difficulty are we trying to prevent?
What evidence suggests it is likely enough to matter?
Can we reduce the risk with a smaller change?
How will we know the preventive change actually helped?
That final question matters. Preventive work should have an expected outcome: slower storage growth, fewer recurring errors, improved test coverage around a dangerous workflow, removal of an unsupported dependency or greater headroom before capacity becomes constrained.
If the team cannot explain what risk is being reduced, the work may be improvement for its own sake rather than maintenance.
Preventive maintenance works best as a small recurring loop rather than an occasional cleanup project.
Observe the application and its environment. Look at production trends, dependency status, recurring incidents, support patterns and known technical weaknesses.
Identify credible risks. Separate evidence-backed concerns from general opportunities to improve the codebase.
Prioritise by consequence and likelihood. A small risk to a critical payment workflow may deserve more attention than a larger inconvenience in an internal reporting tool.
Make the smallest useful intervention. Patch the dependency, add the missing test, increase capacity, improve the query or refactor the fragile section without unnecessarily widening the change.
Measure what happened afterwards. Confirm that the risk was actually reduced and that the preventive work did not create a new problem.
This last step stops prevention from becoming guesswork.
Levelworks' App Health framework provides a useful way to think about what preventive maintenance is trying to preserve.
A rising error rate may threaten Error Free.
An unsupported library affects Secure and Current & Updated.
A database approaching its practical limits threatens Performant and Scalable.
A fragile component with poor tests affects Tested, Explainable & Debuggable and potentially Seamlessly Updatable.
An API approaching retirement affects Integrable and Compatible.
Preventive maintenance does not require checking all 17 App Health Aspects every time an engineer opens the backlog. The framework is useful because it encourages teams to look beyond whether the application is currently online and ask where its health may be weakening.
That is the distinction that matters most.
Reactive maintenance waits for evidence in the form of a user-facing problem.
Preventive maintenance acts when the evidence appears earlier—in the trend line, the dependency report, the recurring incident, the fragile code path or the upcoming platform change.
The goal is not to predict every possible failure.
It is to notice the problems that are already beginning to form, while there is still time to fix them on your terms instead of the user's.