Five plugins, five missing operator paths
In April I published a plugin whose only job is to let a locked-out WordPress administrator back into their own site.
Before it existed, the recovery path went like this. Open a database client. Find the usermeta table. Locate the row holding the user’s capabilities. Hand-edit a serialized PHP string without breaking the byte-length prefixes, because if you break them the string stops parsing and now nobody has access. That was the supported route for someone who had just lost access to the system running their business, usually at the worst hour of the worst day.
I built the rescue link because I had walked people through that edit too many times. For about a year I filed it away as a good plugin idea. That was the wrong filing. It’s a symptom, and once I read it that way I couldn’t stop seeing the same shape in everything else I’d shipped.
The law that named it
Lucas Fernandes da Costa published ten laws of software project management in June. Two of them read like they were written about a support queue.
An engineering decision that works for engineering but fails sales, marketing, pricing, support, or operations is not a good engineering decision.
Operations are not what happens after the project. An operable product is one of the things the project must deliver.
His point on the second one is that monitoring, alerts, documentation, admin tools, and a way to reverse a bad change are all part of the work. Skip them and you’ve delivered something that functions only while nothing goes wrong. The missing pieces get discovered during incidents instead, which is the most expensive hour to build anything in.
I read that and went to look at my own published work with a colder eye than I’d have liked.
Five plugins, five gaps
The rescue link is the clearest case. The product had a way to lose access and no way to get it back, which is a missing rollback path stated about as literally as it can be.
Selective Role Reset is the same failure one step along. The admin tool existed, but resetting roles was all or nothing, so running it gave you clean WordPress defaults plus the destruction of every custom role another plugin had created, plus users stranded on roles that no longer existed. The tool was there. It had no safe mode, so the honest advice was usually to leave the button alone.
Admin Filters and Gift Reporter are both about not being able to see. Once a members list passes a few thousand rows, an operator who needs everyone in one city, or everyone with a particular custom field value, has no way to ask for that in the interface. Gifted transactions had the same problem. Which gifts went unclaimed, who to chase, what the month actually looked like. In both cases the data was sitting right there in the database. The operator’s route to it was a support ticket asking me to run a query.
Staging Safe Mode is the one that still bothers me most, because the failure is so predictable. Clone a production membership site to staging and the clone cheerfully mails real customers, runs real reminder crons, and talks to real payment gateways. Cloning was a supported thing to do. Containing the clone was never part of the deal, so every operator learns about it the same way, which is afterwards.
Five features that worked exactly as specified, each one assuming nothing would go wrong and nobody would ever need to look inside.
One exception, because a pattern that swallows everything isn’t worth much. Forward-Only Access, which gates content by publish date against join date, isn’t operability debt. The product genuinely didn’t do it and some businesses need it. That’s a feature request, and it belongs in a different pile.
The support queue has three piles in it
This is the part I think support engineers can see and product teams mostly can’t.
Escalations get discussed as one number. Volume, backlog, average handle time. Underneath that number there are at least three kinds of ticket, and they have nothing in common except who answers them.
A defect is something broken against its own spec, and defects have a home. You file a bug, it gets prioritized, eventually it closes. That system works fine. A feature gap is the product not doing something a customer wants, and that has a home too, as a request on a board competing for roadmap space honestly.
The third kind has nowhere to go. The feature works exactly as designed and the person running the site still can’t see whether it’s working, can’t fix it when it isn’t, and can’t undo it after a mistake. There’s no bug to file because nothing is broken. There’s no feature to request because from the outside it looks like the operator just needs showing how. So it gets answered one customer at a time, indefinitely.
The themes that recur in my own queue lean heavily on that third pile. Take the grace period after a failed payment, before access is revoked. It’s controlled by a filter called mepr-grace-expire-days, so changing it means writing PHP. That’s a good hook and a defensible engineering decision, and it also means every operator who wants a different grace period has to open a ticket, because no other route exists. File protection breaking under a caching layer works the same way. Nothing is malfunctioning by its own lights, two systems just disagree, and the operator has no diagnostic that tells them which one to look at. Corporate account lifecycle behavior sits in the same place, visible from code and invisible from the admin.
None of those are bugs. All of them are recurring monthly costs. And because they never take the shape of a ticket that closes, they don’t show up in any count of what the product owes.
Why it happens to good teams
Nobody decides to skip this.
The operator isn’t in the room when the feature gets specified. Users sometimes are. Buyers usually are. The person who’ll be running this thing on a Tuesday at 2am is represented by nobody.
The cost also lands on someone else’s budget. Engineering saves two weeks by shipping without the admin screen, support pays for it in perpetuity out of a different line item, and no mechanism connects those two numbers. So the trade never gets evaluated as a trade.
Then there’s the reason I think actually decides it. This work is invisible when it succeeds. An admin tool that stops a whole category of ticket from ever being opened produces no evidence of its own value, because the tickets it prevented don’t exist. Nothing to point at in a review, nothing to put in a changelog.
Four questions that would have caught all five
I don’t think this needs a process. Four questions, asked once, before the thing gets built, with the person who’ll operate it in the room.
Who operates this, and are they in this conversation?
How will they know it’s working? Not how we’ll know, from logs. How they’ll know, from inside the product.
How do they fix it when it isn’t working, without a database client and without us?
How do they undo it after doing the wrong thing, which they will?
Run the rescue link against those and question four has no answer, and the gap is visible before a line of code exists. Run Staging Safe Mode and question three catches it. Every one of my five plugins is a question nobody asked, answered years late by someone outside the team that shipped it.
The collections department
There’s a version of this post where I’m pleased about the plugins. Five useful things exist, operators are better off, some of them are on WordPress.org where anyone can install them. All of that is true.
It’s also the most expensive possible route to here. Each one took weeks, arrived years after the feature it patches, and only covers the specific case I got annoyed enough to fix. The same work at design time would have been days, would have shipped with the feature, and would have covered cases I still haven’t thought of.
What’s changed for me is how I read my own escalation volume. A queue isn’t only a measure of quality. A good part of it is a readout on which features shipped without an operator, and that part barely moves over time, because nothing in the normal process ever pays it down. It gets absorbed instead, monthly, by whoever is standing closest.
For five years that was me, and I got five plugins out of it. I’d still rather have had the four questions.
Have a similar challenge?
I help SaaS companies and WordPress platforms solve their most complex technical problems.
Let's Talk