Product

The maintenance tax nobody prices in when killing features

How to find the features costing you more than they return, what the real carrying cost of a feature is, and a deprecation sequence that does not burn trust.

Nine per cent of accounts, forty per cent of the support queue

A team I sat with in Žižkov had an integration used by 34 of their 380 paying accounts. Nine per cent. It generated, by their own tagged ticket counts, a bit under forty per cent of support volume, because the third-party API changed shape roughly quarterly and the failures were silent. Two engineers had touched it in the past year. Neither wanted to.

They had discussed removing it four times across eighteen months and never done it. The reason was always the same and always sounded responsible: some customers rely on it. True. It was also, on the numbers, the single most expensive thing in the company, and nobody had ever written that sentence down.

That is the pattern. Features are added with a business case and removed with an argument. The removal loses, because the cost of keeping is diffuse and the cost of removing is a specific person being annoyed on a specific Tuesday.

What a feature actually costs to keep

The build cost is the part you paid and forgot. The carrying cost is the part you pay forever, and it has at least six components that never appear on a roadmap:

  • Test surface. Every code path is a set of cases that must keep passing, and slower CI on every commit anyone makes.
  • Cognitive load in the codebase. Every engineer touching adjacent code must understand it well enough not to break it. That tax is paid per engineer per encounter, forever.
  • Interface real estate. Every menu item makes the other menu items harder to find. This is the cost nobody prices, and it is charged to your new users, who are the people least able to absorb it.
  • Support and documentation. Tickets, help articles that drift out of date, onboarding time for new support hires.
  • Constraint on future work. The refactor you did not do, the schema you could not change, the dependency you could not upgrade. Usually the largest term and always invisible.
  • Vendor and infrastructure line items. Small, but at least it is countable.

Here is a crude estimate I use to make the conversation concrete. Take engineering hours logged against the feature in the last twelve months. Add support hours from tagged tickets. Multiply by loaded cost. Then add one engineer-week per feature per year as a floor for the ambient tax of it simply existing. Put that number next to the revenue that would genuinely leave if it vanished — not the revenue of accounts that touch it, the revenue of accounts that would cancel over it, which is a much smaller and much harder number.

The estimate will be wrong. It will still be a hundred times better than the current method, which is vibes and loss aversion.

Find the candidates in an afternoon

Instrument usage at the feature level, not the page level, and pull ninety days. For each feature you want three numbers: distinct accounts that used it at all, distinct accounts that used it more than three times, and the share of your top-decile-revenue accounts among them.

Then sort by a simple ratio: accounts using it more than three times, divided by your best guess at annual carrying cost. The bottom of that list is your kill list.

Two things will surprise you. The first is how many features have single-digit account counts — in most products I have audited, somewhere between a fifth and a third of shipped surface area is used by under 5% of accounts. The second is that some low-usage features are load-bearing for revenue: three accounts, but they are your three largest, and one of them bought because of it. Those you keep, and you should know their names.

A feature nobody uses that costs nothing to keep is not urgent. Sort by cost, not by loneliness.

The deprecation sequence

Once you have decided, the way you do it determines whether you lose the feature or the customer. Here is the sequence I use.

  1. Stop new adoption first. Hide it from accounts that have never used it. Zero risk, immediate stop to the bleeding, and it makes the eventual removal smaller. Do this the day you decide, before any announcement.
  2. Contact the users individually. Not a banner. Not a changelog post. If it is under two hundred accounts, an email from a real person. Under thirty, a call. Ask what they use it for. You will find that a third of them use it for something you did not intend, which is either a new product or a cheaper replacement.
  3. Name the replacement or say there isn't one. "Use the API instead" is a replacement if you also show them the three lines of code. Vagueness here is what turns a deprecation into a betrayal.
  4. Give a date and honour it. Ninety days for something workflow-critical, thirty for something peripheral. A date you move once teaches everyone that all your dates are soft, and the next deprecation costs you double.
  5. Degrade before you delete. In the final fortnight, make it visibly deprecated in the interface, with the date. People who ignored three emails read the banner in the app.
  6. Provide an exit with their data. An export, in a format that opens somewhere else. This single step converts most of the anger. It says the relationship survives the feature.
  7. Delete the code, not just the entry point. Hiding the button and leaving the code is the worst of both worlds: you keep every carrying cost and gain no clarity. Remove the routes, the tables, the tests, the vendor account, the help articles. Do it in one commit with the ticket linked, so the next person can find out why.

When to buy your way out

Sometimes the honest answer is that ten accounts genuinely depend on this and they pay you enough to matter. You have three options and all are legitimate: build the migration for them by hand, price the feature separately at what it actually costs to carry, or let those accounts go with a refund and a warm referral to a competitor that does it well.

That last one reads as failure and usually is not. A customer who leaves well and speaks well of you is worth more than a customer you keep by carrying an integration that slows every release. I have watched a founder refund four accounts a combined €11,000 to remove a feature and get the engineering week back permanently. It was the highest-return €11,000 in that year's accounts.

The rule that prevents the next one

Every feature you ship gets a sunset review date written in the ticket. Twelve months out. On that date, someone pulls the usage numbers and either renews it explicitly or starts the sequence above.

This sounds bureaucratic and takes about fifteen minutes per feature per year. What it actually does is change the default. Right now your default is that everything shipped lives forever unless someone summons the courage to argue for its death. With a review date, the default is that it needs a reason to continue. That inversion is the entire mechanism, and it is the only reliable one I have found.

Start here

Open your analytics tonight and list every feature used by fewer than 10% of your active accounts in the last ninety days. Do not decide anything yet. Just put a rough annual carrying cost next to each one, using the crude estimate above, and take the worst offender to your next team meeting with both numbers on the same line.

The conversation changes the moment the cost is written down. In my experience the team already knows which feature it is. They have been waiting for someone to make it acceptable to say.

If this was useful

One letter a month, in the same register as this.

What I am seeing across the weekends: what is working in growth engineering, what stopped working, and the numbers behind both. No sequence, no upsell ladder, and one click to leave.

Monthly. Unsubscribe in one click. Never sold or shared.

Eight founders spend two days in Prague working through exactly this, on their own product.