Code Agency
15 min readBy Fabio Tielen

What a care plan should contain: maintenance contracts worth signing

Most maintenance contracts are a monthly invoice with a promise attached. The line items that make one real — patch cadence, backups you have restored, an SLA that says what a clock is and when it runs, and hours you can actually spend — plus the questions that expose a hollow one in five minutes, applied to ours as well as everyone else's.

A care plan is the easiest thing in this industry to sell badly. It is a recurring invoice for work that, when everything goes well, is invisible — which means the gap between a genuine contract and a hollow one only becomes obvious on the worst day of the client's year.

We've inherited a lot of applications from other suppliers, and the pattern repeats. There was a maintenance contract. It had been paid, on time, for years. Nobody had ever tested whether the backups restored, the "monitoring" was an uptime pinger on the homepage, and "security updates" meant WordPress core auto-updated itself while eleven plugins sat two major versions behind. The client had not been cheated in any way a lawyer would recognise. They had bought a document that described activity rather than outcomes.

So here is the list of what actually has to be in one, written the way we'd want to read it if we were the ones signing. It applies to ours too — the last section says what our own plans don't cover, because a care plan that claims to cover everything is the clearest signal you're reading marketing.

The rule that makes the rest of the list readable

A line item is only real if it produces an artefact.

That's the whole filter. "We monitor your application" produces nothing. "You get a monthly report listing patches applied, incidents opened and closed, backup restores tested, and hours consumed against your pack" produces something you can read, file and argue with. Every item below is written to that standard, and when you're reading someone else's offer, the fastest way through it is to ask, per line: what lands in my inbox, and how often?

The second-fastest is to ask what happens when the item fails. A contract that describes only the happy path hasn't been written by anyone who has run production.

Updates: which layers, on what cadence, and who tests

"Updates included" is the most common line in the industry and the least informative. There are at least five separate layers in any real deployment, and a contract that doesn't distinguish them is covering whichever one is cheapest.

  • Application dependencies — npm, pip, Composer, Odoo modules. The high-volume layer, and the one where supply-chain attacks arrive. Should be automated with a bot that opens pull requests, not a human who logs in quarterly.
  • Runtime and framework — Node, Python, PHP, Next.js, Odoo major versions. Low volume, high impact, needs planning and a maintenance window. A major upgrade is usually a project, and an honest contract says so rather than pretending it's included.
  • Container base images and OS packages — where the CVEs you'll be asked about in a security questionnaire actually live. Nobody notices this layer until an auditor does.
  • Infrastructure — Kubernetes, the database engine, the ingress controller. Invisible to the client, and entirely the supplier's job.
  • Third-party services — the payment provider deprecating an API version, the mail platform changing its authentication requirements. Not "updates" in the patch sense, but the same category of work: something outside your control changed and someone has to respond.

A good contract names the layers it covers and the cadence per layer, and it says what happens when an update breaks something. That last clause is the tell. Updates that are applied without a rollback path aren't maintenance, they're a scheduled outage waiting for a bad week.

Our own answer is that dependency updates run through Renovate on our own infrastructure — grouped, on a schedule, with a minimum release age so we're never the first to install a compromised package — and land as pull requests that build and run the test suite before anyone looks at them. That's not a virtue, it's the only version of this that scales past three clients. But it's the level of detail you should be able to get out of any supplier in one question.

The security patch is the exception that needs its own line. A critical CVE in a public-facing component doesn't wait for the monthly window. The contract should say how fast you move on one, and — the part people forget — that you're allowed to move without waiting for the client's approval. We've watched a supplier sit on a fix for nine days because their contract said "changes require written client approval" and the client's IT contact was on holiday.

Monitoring: what is watched, and who gets woken

There's a hierarchy here, and most contracts stop at the first rung.

  1. Is the site up? An HTTP check against the homepage. Cheap, near-useless on its own. It stays green while the checkout is broken, the database is read-only, and the nightly job hasn't run in a week.
  2. Are the important paths working? Synthetic checks that log in, add to cart, submit the form. This is where uptime monitoring starts to mean something to the business.
  3. Is the application healthy? Error rates, queue depth, job failures, response times, certificate expiry, disk. We run self-hosted metrics and alerting for this, and error tracking so an exception has a stack trace and a user attached rather than being a spike on a graph.
  4. Is it getting worse? Trends, not thresholds. The slow disk fill, the query that has gained 40 ms a month since March. This is the one that separates maintenance from firefighting, and it only exists if someone actually looks at the dashboards on a schedule.

Then the question that matters more than any of it: when something fires at 03:00, what happens? A dashboard nobody watches is not monitoring. A contract should say whether alerts route to a person out of hours, and if they don't — which is a perfectly legitimate answer for a business application whose users are all asleep — it should say that too, plainly. We don't claim 24/7 support and we won't write it into a plan; what we will write is which alerts page someone, which ones wait for the morning, and what our hours actually are.

Ask to see the alert list. If nobody can produce one, nothing is being watched.

Backups: the only line item with a pass/fail test

Everyone includes backups. Almost nobody includes restores, and a backup that has never been restored is a hypothesis.

The five things a contract has to state:

The questionThe answer that means something
ScopeWhat's in it?Database and filestore and uploaded media and configuration. Missing the filestore is the classic — you restore the database and every invoice PDF and product image is gone.
RPOHow much data can we lose?An hour of transactions is a very different product from a day's. Nightly-only is fine for a brochure site and negligent for a webshop.
RTOHow long until we're back?Must be a number someone has measured, not one someone hopes for.
RetentionHow far back can we go?Ransomware and silent corruption are found late. Daily-for-a-month plus monthly-for-a-year is a reasonable shape; seven rolling days is not.
IsolationCan the thing that killed production kill the backups?Different credentials, different location, ideally immutable. A backup your production credentials can delete is a snapshot.

And then the one that turns all five into a fact rather than a claim: a restore drill on a schedule, with the result written down. We run these monthly and the report says how long it took. It is the single most valuable line in any care plan we sell, and it's the one clients ask about least — right up until the day it's the only line that matters.

If an offer in front of you doesn't mention restore testing, that's not a gap in the document. It means it isn't happening.

Response times: an SLA that survives contact with an incident

Most SLAs are a table of severity levels and response times. Fine, as far as it goes — but three definitions decide whether the table means anything.

What is a "response"? In a weak contract it's an automated acknowledgement e-mail. Define it as a human confirming they've read the ticket and stating what happens next, or you've bought a mail server.

When does the clock run? "Four hours" during business hours on a Friday afternoon is Monday lunchtime. That's a defensible answer, but it has to be written down, and the business hours have to be stated in a timezone. A ticket raised outside those hours starts its clock when they open.

Who decides the severity? If it's the supplier, everything is a P3. If it's the client, everything is a P1 and the levels stop meaning anything. Write the definitions against business impact — "production down, no workaround", "core process degraded, workaround exists", "cosmetic or scheduled" — so the label follows from the facts and there's nothing to argue about at the point where arguing is most expensive.

Two more things worth having, in ascending order of how rarely you'll see them:

A resolution target alongside the response time. Not a guarantee — nobody can guarantee the time to fix an unknown bug, and a supplier who promises one is either lying or padding — but a stated target with an obligation to communicate at intervals until it's met. The complaint after a long incident is almost never "it took too long", it's "nobody told us anything for six hours".

A penalty, or an honest admission that there isn't one. Uptime SLAs with service credits are standard in hosting — ours is a written 99.9% on the infrastructure we run ourselves — and they're worth having, though be realistic: a credit refunds a fraction of a monthly fee against a day of lost revenue. Its real function is to make the supplier's incentives visible. A supplier who won't put any number in writing is telling you something.

Watch for the scope trick, too. An uptime SLA on hosting is not an SLA on your application. The infrastructure can be at 100% while your app throws 500s on every request. Ask which one the number refers to.

Hours: the difference between a care plan and a support pack

This is where most of the actual money goes and where contracts are vaguest.

There are two honest models. Maintenance is the recurring work of keeping what exists running: patches, monitoring, backups, incident response. It's a fixed monthly fee because the work is predictable. Support and small changes is everything else — the new report, the field added to a form, the "can you just" — and it's consumed hours, because that work is not predictable and pretending otherwise means someone builds a margin for fear into your monthly fee.

The problems all come from blending the two badly:

  • Unlimited support. Either it isn't, or the price includes a buffer for the worst client on the roster and everyone else subsidises them.
  • Hours that expire monthly. A near-universal clause and mostly a revenue mechanism. Real work is lumpy: nothing for two months, then a fortnight of it. Quarterly pooling with a sane cap is the fair version.
  • No visibility until the invoice. You should be able to see hours consumed and what they were spent on, as they're spent. Ours run through the helpdesk and the timesheet-to-invoice trail is per-minute, which means the conversation at renewal is about a list rather than a feeling.
  • No rate for overflow. What happens when the pack is empty, at what price, and who has to approve it before work continues.

We sell it as support packs against an active support agreement — tickets from someone without one can't be scheduled, which sounds bureaucratic until you've watched an unfunded "quick favour" queue push a paying client's incident down the list. The shape matters less than the fact that it's written down.

The line items that reveal a hollow contract

Some clauses only ever appear when someone has thought about the relationship ending, or about what happens when they're not the ones who fix it. Their absence is the most reliable signal in the document.

  • Access to your own things. Repository, servers, DNS, registrar, CDN, mail platform, analytics. You should hold owner-level access to all of it, with the supplier as a collaborator. If leaving requires them to hand something over, you don't own your business's infrastructure — you're renting it from someone whose interests diverge from yours the moment you consider a change. Everything we build, once paid for, is yours, and that includes the keys.
  • Exit terms. Notice period, what gets handed over, in what format, and whether transition help is billable. Write it while everyone still likes each other.
  • Documentation kept current. Not a wiki written at go-live and never touched. A short runbook — how to deploy, how to restore, what the scheduled jobs do, who to call — updated when it changes. This is the thing that decides whether your next supplier needs three weeks or three months to become useful.
  • A named human. Not a shared inbox. Someone who knows the system, plus a named backup, because one person is a single point of failure with a holiday calendar.
  • A periodic review. Twice a year, an hour, looking at incidents, hours, upcoming end-of-life dates and what should change. Without it a care plan silently drifts into a subscription nobody evaluates.
  • What's explicitly excluded. The best sign in any offer. A contract with no exclusions section hasn't been thought about; the exclusions are where the supplier admits what the fee doesn't buy, and reading them is faster than reading the inclusions.

The five-minute test

Take the offer in front of you — ours included — and ask these. The answers arrive fast, and the hesitations are as informative as the answers.

  1. When did you last restore this system from a backup, and how long did it take? The best question in the set. There is no way to bluff a specific date.
  2. Show me last month's report. If reporting is a line item, an example exists.
  3. A critical CVE drops in a public-facing component on a Saturday. Walk me through the next four hours. You're listening for whether they can act without you, and whether anyone is actually looking on a Saturday.
  4. Which layers do the updates cover, and when did the runtime last move a major version? "Everything is up to date" is not an answer. A date is.
  5. If we left in six months, what would you hand over and what would it cost? Watch the reaction to the question, not just the answer.

Two more if you're comparing plans on price: what's excluded, and what happens to unused hours. Most of the gap between a cheap plan and an expensive one lives in those two answers rather than in the headline items, which are near-identical across every offer you'll be sent.

What ours doesn't cover

Since the whole post argues that the exclusions are the honest part of a contract, here are ours.

Major version upgrades are projects, not maintenance. Moving an Odoo database across a major version, or a framework across a breaking release, is scoped and quoted separately. We flag the end-of-life date well ahead and put it on the review agenda, but the fee for keeping something running doesn't fund rebuilding it. Anyone including that in a monthly plan has either priced it in silently or isn't planning to do it.

We don't maintain code we can't update. A plugin abandoned four years ago, a module written by a supplier who won't share the source, a dependency pinned to a version with an open CVE and no patch — we'll tell you, we'll propose a replacement, and we won't pretend the risk is managed by a line in a contract. The same standard we apply to community modules before they touch production applies to what we agree to keep alive.

We don't guarantee a fix time, only a response time, a resolution target and an obligation to keep talking until it's closed. See above.

We don't claim 24/7. Alerts route to a person outside office hours for infrastructure that needs it; a request for a content change at midnight waits until morning. Both facts are written into the plan rather than implied by a badge on a webpage.

The short version

A maintenance contract is worth what its artefacts are worth. Patch reports, restore-drill results, monthly summaries, an alert list, a runbook, a per-minute record of the hours you bought — each one is a thing you can hold, and each one corresponds to work that either happened or didn't. Everything else in the document is adjectives.

The five failure modes are always the same: updates that cover one layer and imply five, monitoring that only knows whether the homepage returns 200, backups nobody has restored, an SLA whose clock and severity levels are undefined, and hours that expire before you can use them. If a plan is clean on those five and says out loud what it excludes, it's probably a real one, whoever wrote it.

That's the standard we hold our own plans to. We build the application, we run the infrastructure it lives on, and the same team takes the ticket when something breaks at 07:40 on a Tuesday — which is the underlying reason we can write specifics into a care plan instead of adjectives. If you're holding a maintenance offer from someone else and want a second opinion on it, send it over; the five-minute test above works just as well when we're the ones being asked.

Want us to publish something specific?

Tell us what you'd like to read and we'll add it to our writing queue.

Get the next one in your inbox

New articles, videos and the occasional engineering note — a short mail when there’s something worth reading, nothing else.