The real cost of maintaining custom software after launch
What maintaining custom software actually involves after launch, the outside deadlines that force the work, and how to budget for it honestly.
The app went live, the team is using it, and the developer's invoice has been paid. Then, a few months later, a quote turns up for "maintenance" and nobody quite knows what they're paying for. Nothing is broken. Why does working software need ongoing money?
Because the world around it keeps moving. The language it's written in has a support end date. The libraries it depends on publish security fixes. The hosting platform retires old options. Your staff ask for changes. None of that is optional for long, and almost none of it shows up in a build quote. Here's what the work actually consists of, which deadlines you don't control, and how to put a sensible number on it before you commit.
Most maintenance isn't fixing bugs#
The IEEE's software engineering body of knowledge (SWEBOK) uses four categories for post-launch work, drawing on the IEEE 14764 maintenance standard. Paraphrasing its chapter on software maintenance:
| Type | What it means | A small-business example |
|---|---|---|
| Corrective | Repairing problems found after delivery | The export breaks on names with apostrophes |
| Adaptive | Keeping the software usable as its environment changes | Moving to a supported Python version, or a new OS image on the server |
| Perfective | Enhancements for users, plus better docs or performance | Adding the field finance asked for, speeding up a slow report |
| Preventive | Finding and correcting latent faults before they bite | Adding tests around the billing code before someone else edits it |
The same chapter cites surveys finding that over 80 percent of maintenance goes on noncorrective work. That figure is worth holding onto. If you think of maintenance as "paying for the developer's bugs", you'll underbudget and resent every invoice. Most of it is adapting to change and adding things people want. A tool that's genuinely useful attracts requests, so the busier it is, the more perfective work it generates.
SWEBOK also says that maintenance "consumes a major share of the financial resources in a software life cycle". It doesn't put a single percentage on that, and you should be wary of anyone who quotes one confidently for your project. The share depends on how long the tool lives and how much it changes.
The clocks you don't control#
Adaptive maintenance is the part people forget, because it's driven by other organisations' schedules. A few examples are useful because they're concrete and published.
Language runtimes have fixed lifetimes. The Python developer's guide says that after two years a release only receives security fixes, and five years after release support ends entirely. Python 3.10 reached end of life on 1 October 2026. If your internal tool was built on it a few years ago, it's now running on a version that gets no more fixes, and 3.11 is scheduled to follow in October 2027.
Node.js works on a shorter cycle. Its release page says long-term support releases typically get critical fixes for a total of 30 months, and that production applications should only use Active LTS or Maintenance LTS releases. From Node.js 27 the project moves to one major release a year. Node.js 20 is already on its end-of-life list, and that page is blunt about what this means: no more updates, including security patches. It also warns that popular packages drop support for old versions over time and that many audits forbid unmaintained runtimes. So staying put eventually blocks you from updating your other dependencies as well.
TLS certificates are getting shorter-lived. The CA/Browser Forum passed a ballot that cuts the maximum validity of public TLS certificates from 398 days to 47 days, in steps running from March 2026 to March 2029. If anyone is still renewing a certificate by hand once a year, that habit has an expiry date too. Automated renewal stops being nice to have.
Security patches come with expected turnaround times. The Australian Signals Directorate's Essential Eight maturity model sets, even at its lowest maturity level, that patches for vulnerabilities in online services are applied within 48 hours when the vendor rates them critical or a working exploit exists, and within two weeks otherwise. It also says online services no longer supported by vendors are removed. You may not be formally assessed against it, but it's a reasonable yardstick. If a framework your customer portal depends on publishes a critical fix on a Friday, someone needs to be able to test and deploy it the following week, not at the next quarterly catch-up.
Add your hosting provider's deprecations and any third-party API you call (payments, email, accounting software), each with its own version retirement notices, and you have a steady stream of work that arrives whether or not anyone wants a new feature.
Where the hours actually go#
When a change does land, the expensive part usually isn't typing the fix. SWEBOK cites research suggesting about half of total maintenance effort goes on understanding the software before changing it. It names the lack of documentation and test environments as a leading cause of difficulty, and notes that repeating full testing on a significant piece of software costs real time and money.
That has a practical consequence for small businesses. The cost of a one-line change depends heavily on whether the next developer can find the code, run it locally, and know what else it touches. A tool built quickly by one person, with no tests and the deployment steps living in their head, is cheap to launch and expensive to change. The bill for that shortcut arrives later, often when the original developer has moved on and someone new has to reverse-engineer the system first.
SWEBOK also notes that software grows more complex as it evolves unless someone takes deliberate action to reduce that complexity. Every quick fix that skips tidying up makes the next change a little slower.
How to budget for it honestly#
You won't get a precise figure, but you can get a defensible one. Start by listing the recurring work rather than guessing a percentage.
- Write down every external dependency with a lifecycle: language runtime, framework, database, operating system or container base image, hosting platform, and each third-party API. Note each one's published support end date where there is one.
- Count the routine jobs: dependency and security updates (monthly is a sensible minimum), certificate and domain renewals, backup restore tests, and checking logs or alerts.
- Add the forced upgrades. A runtime with a five-year life that's already two years old will need a version upgrade within the period you're budgeting for. Ask your developer how long that upgrade would take for this codebase.
- Estimate perfective work from experience. If staff have asked for six changes in the first three months, they won't stop at month four.
- Put a person's name against on-call response. Who deploys a critical security fix within days, and what does that cost per incident?
Then add hosting and any paid services on top. Ask for the estimate in hours per month or per quarter, because hours are easier to sanity-check than a lump sum. If a developer can't explain which line items make up their maintenance quote, that's worth a conversation before you sign.
Ways to make maintenance cheaper#
Some decisions at build time reduce the ongoing cost a great deal, and they're worth paying for up front.
Choose long-term-support versions of everything, and record their end dates somewhere visible. Keep the dependency list short, since each library is another changelog to watch. Turn on automated dependency update pull requests so small, routine upgrades happen continuously instead of piling up into a painful migration. Automate certificate renewal now, before the 47-day limit makes manual renewal impractical.
Insist on a basic automated test suite around the parts that handle money, customer data or anything you'd be embarrassed to get wrong. Tests are what make a two-week patch window achievable. Ask for a short README that explains how to run the app locally, how to deploy it, and where the secrets and accounts live, all owned by the business rather than the developer. At TheAILAB we treat that handover document as part of the build, because it's the cheapest way to cut the "understanding" half of every future change.
Put the dates in a calendar#
The most useful single step is also the simplest. Take the end-of-life dates for your runtime, framework, database and operating system, put them in a shared calendar with a reminder six months ahead, and do the same for certificate renewals and any API version retirements you've been warned about. Then book a short review each quarter to check the list. Maintenance gets expensive when it's discovered as an emergency, and nearly every forced upgrade on that list was announced well ahead.
Sources
- Chapter 5: Software Maintenance — IEEE Computer Society, SWEBOK Guide (wiki edition) (checked 2026-10-19)
- Node.js Releases — OpenJS Foundation / Node.js project (checked 2026-10-19)
- End-Of-Life (EOL) — OpenJS Foundation / Node.js project (checked 2026-10-19)
- Status of Python versions — Python Software Foundation, Python Developer's Guide (checked 2026-10-19)
- Ballot SC081v3: Introduce Schedule of Reducing Validity and Data Reuse Periods — CA/Browser Forum (11 April 2025) (checked 2026-10-19)
- Essential Eight maturity model — Australian Signals Directorate (last updated 27 November 2023) (checked 2026-10-19)
Dealing with this in your own business?
Tell us what you're working on and we'll say whether it's worth automating.