We billed them for the cleanup
Maintenance is a hard sell when the product is nothing going wrong, and a 15-minute note each month might be what gets it renewed.
Early last year, one of the few WordPress sites at my old agency spent about two months redirecting phones somewhere else. I’d built its front end in 2018. Desktop was fine. Everybody who touched that site, on either side, checked it on a laptop. I think that’s most of why it was two months and not two days.
We found out because a customer emailed the client to ask why their site kept sending them to a page about a sweepstakes. Not from monitoring (there wasn’t any), and not from us.
$ find wp-content/uploads -name '*.php' -printf '%TY-%Tm-%Td %p\n'
2021-02-17 wp-content/uploads/2018/09/wp-conf.php
2021-02-17 wp-content/uploads/2019/04/class-wp-cache.php
2021-02-17 wp-content/uploads/2020/11/index.php
Three PHP files in the uploads folder, all written the same night in February. Nothing in that folder is ever supposed to end in .php. It holds the pictures.
The way in was a plugin about two years behind on updates, with a hole that had been published and patched long before any of this. Something automated found it, the kind of thing that reads the entire internet looking for exactly that plugin at exactly that version. That whole category of problem has had its own entry on the OWASP list for as long as there has been a list, under the least dramatic name on there.
Nobody picked that site. It answered a knock.
We cleaned it up over a couple of days and then invoiced for those days. That invoice came to comfortably more than a year of the maintenance nobody had ever offered them. I don’t think a single person on the team found that strange, me very much included.
Nobody did anything wrong at any point, and a year and a half later that’s still the part I keep coming back to. We built what was scoped. They paid what was quoted.
Nobody bought maintenance because nobody sold it, and I wouldn’t call that negligence. It’s the shape of the thing, the same shape as a homepage that grows to 2.4 MB because people changed the picture like they were supposed to be able to. Everyone behaves correctly and the outcome is bad anyway.
Nothing to photograph
From our side, the incentives all point in one direction. I say that as somebody who benefits from them.
A build is a project. It has a start, an end, a scope, a fixed price and a screenshot for the website.
Maintenance has none of that. It’s open-ended and the effort moves around from month to month. There’s nothing to photograph at the end of it.
New business is also what gets celebrated. I’ve sat in plenty of weekly meetings where somebody won a build and the room clapped. I’ve never once heard anyone announce that a retainer had renewed for the sixth year.
But the product is a non-event, and that’s the real difficulty. You’re selling the absence of a problem. When it’s working, the client’s experience of it is identical to not having bought it. After 18 months of nothing happening, the line item starts to look like a streaming service somebody forgot to cancel.
Like the office carpet
The client side is just as reasonable, which makes it hard to argue with.
The site is done. It launched and everyone was pleased. Now it’s a thing that exists, like the office carpet.
Nobody has a mental model where the carpet has a version number and a support window. And nothing about how we sell these sites, deliver them or hand them over does anything to correct that.
The money is in the wrong shape too. A build is one amount that gets approved once, usually out of a budget that exists for projects. Ongoing monthly spend goes through a completely different door. It gets reviewed every year by somebody who wasn’t in the original room, and it has to survive next to line items with visible outputs.
And the word itself has been spent. Nearly every client I’ve talked to about this has paid somebody a couple hundred a month before and gotten back an automatically generated PDF listing plugin updates, plus a traffic chart. It all reads as very deliverable-coded. So “maintenance” arrives already meaning money for nothing, and I can’t really argue because I’ve read those reports.
Everything with a date on it
“Things decay” has never persuaded anybody of anything, so here are the specifics.
Security updates. Everybody names that one, and it’s the one that got that 2018 site.
The platform underneath. The language runtime has an end-of-support date, published years in advance. When it passes, the host moves the site onto a newer version on their own schedule, sometimes with a couple weeks’ notice. If the code isn’t ready, the site breaks.
That’s the most predictable failure on this list and the one that surprises clients most, because it comes from a direction they didn’t know was there.
Integrations. The payment provider deprecates an API version. The mailing platform moves an endpoint. The map service starts requiring a billing account. Every one of those is somebody else’s roadmap landing on a day you didn’t pick.
The web changing around it. Cookie rules tightened, SameSite defaults changed and browsers started blocking things they used to allow. A site that worked at launch stops doing one specific thing and nobody notices for months, same as the phones.
The other half of that is the code nobody takes back out. A browser went out of support last month, and that turned up a polyfill in a client template that had been doing nothing since about 2016.
Certificates, domains and DNS. They still cause more emergencies than code does.
Packages nobody chose, four levels down a dependency tree nobody reads. They haven’t gotten better since.
And the content. Dead links, a team page listing 3 people who left, a notice about opening hours from 2020. Nobody files any of that under maintenance, but it’s the part customers actually see.
Write down the nothing
Of everything here, this is the one I’d actually do first.
Retainers get canceled because nothing visible happens, so make the nothing visible. Send a short note every month, in plain words, saying what was done and what didn’t happen as a result.
June
- Applied 14 updates. Three were security fixes, and one of those
was for a hole being actively exploited on other sites right now.
- Restored May's backup into a test environment and it came back
clean. We do this quarterly, because a backup nobody has restored
is a rumor.
- Submitted the contact form and the quote form. Both arrived.
- Checked the certificate. It renews automatically on October 12.
- The version of PHP this site runs on stops getting security
patches in about 14 months, so it's worth planning that upgrade
for next spring instead of doing it in a hurry.
That takes about 15 minutes to write and I think it’s most of the difference between renewing and not renewing. I’d guess it’s the highest-return quarter of an hour anywhere in this line of work. It isn’t close.
The last line of that note is doing something extra. A client told 14 months ahead of a platform upgrade experiences you as somebody looking after them. The same client, told with three weeks’ notice, experiences an emergency invoice. It’s the same work either way.
What I’d want in the proposal
I don’t write the proposals. When I’ve been asked, though, this is roughly what I’ve argued for. The first part is just saying a number out loud with a reason attached.
The rough convention across software is somewhere around 15 to 20% of the build cost per year for ongoing support. Quoting it helps because it’s a figure the client can go and check instead of a number that came from us.
Then there are two things that aren’t strictly maintenance. The first is a small bank of hours every month for little changes, to spend on whatever they want.
That matters more than it sounds. It turns a grudge purchase into something they actually use, which keeps the relationship warm. So when something does go wrong, you’re already somebody they talk to instead of somebody they have to call.
The second is an annual written review of what’s coming, what’s aging and what to budget for. It’s one page, and the only document in the whole arrangement that’s clearly worth money on its own.
I knew about the PHP version
I’m not the one who sells any of this. Retainers get sold by people whose job is selling. For most of my career I’ve been quietly glad about that, in a way I’ve never examined very hard.
Because I am the one who knows. I can read a lockfile. I can see that the runtime has 14 months left on it, that a plugin hasn’t been touched since 2019, that the certificate is manual and the person who used to renew it left in the spring.
None of that is a mystery. It’s half an hour and a terminal. And mostly I’ve written it into a ticket or said it once on a call, then let it go and called that staying in my lane.
Which is a very comfortable thing to believe when the alternative is a difficult conversation with somebody’s finance department in it. I tell myself that conversation would feel like selling fear. That story does a lot of convenient work for me. It lets me skip the hard part and file the skipping under integrity.
It isn’t fear, though. Fear is “you’ll get hacked.” The true version is more like, “The PHP this runs on stops getting security patches in 14 months. Here’s what the upgrade costs in the spring, and here’s what it costs in a panic in December.” Nobody has to be frightened for that to be worth hearing.
And now I’ve seen the other version up close. Nobody says it and the work happens anyway, about a year late and at a much worse price. We send the invoice and it gets paid without a peep, because by then there’s no decision left in it for anyone to make.