Killing a Feature Is a Graph Problem, Not a Product Problem
Here's the claim that gets people arguing in Slack threads at 11pm: you cannot decide whether to kill a feature by looking at its usage metrics.
Usage metrics tell you who touches the front door. They tell you nothing about what's load-bearing in the basement. I've watched teams sunset a feature with 2% MAU and take down three internal tools, a partner integration, and a compliance report that finance runs quarterly - none of which showed up in any dashboard because none of them are "users" in the analytics sense.
The counterargument, which I think is wrong but is held sincerely by smart people, is that low adoption is itself sufficient evidence. If almost nobody uses it, the blast radius must be small by definition. This is the assumption that costs companies six-figure incident cleanups.
Adoption and dependency are orthogonal. A feature can have terrible end-user adoption and still be structurally load-bearing, because the things depending on it aren't end users - they're other systems, other teams' automations, and legacy contracts nobody re-reads.
The worked example
A mid-size B2B SaaS company - I'll keep the specifics anonymized but the numbers are real - had a Custom Export Templates feature. Built three years prior, touched by 1.4% of active accounts per month, costing an estimated 140 engineering hours per quarter in maintenance (bug fixes, a migration it kept needing, on-call incidents). By any adoption-based scoring, this was a top candidate for deprecation. Classic zombie feature.
The product team had a deck ready. Someone ran a dependency trace before the kill-off instead of after. Here's what the actual graph looked like:
- 1.4% of accounts used the UI directly.
- The export templates fed into a webhook system used by 9% of accounts indirectly, because a different, more popular feature (
Scheduled Reports) silently relied on the same underlying template engine for formatting. - The billing team's invoice reconciliation script - internal, not customer-facing, written by someone who left the company two years earlier - parsed output from this engine to validate export completeness for enterprise contracts.
- Two enterprise contracts (worth roughly $380k ARR combined) had this export format written into their SOW as a deliverable capability, even though the named feature wasn't what the contract referenced - it referenced "data portability," which sales had mapped to this feature during the deal.
Total actual blast radius:
- 1 UI surface
- 1 dependent feature used by 9% of accounts
- 1 undocumented internal script tied to revenue recognition
- 2 contracts worth $380k ARR
None of that is visible from a usage dashboard. All of it is visible from a dependency graph, if you build one before you swing the axe.
The team's revised plan: they didn't kill the feature. They killed the UI for direct usage (saving maybe 20% of the maintenance cost) and kept the template engine alive as an internal-only dependency with a smaller, clearer maintenance contract. Net savings: roughly 30 hours/quarter instead of a nonsensical "140 hours saved" that would have actually cost them an enterprise renewal cycle and an emergency billing hotfix. That's the difference between a blast radius map and a spreadsheet sort by MAU.
The failure mode
Here's where teams get hurt even when they do try to map dependencies: they map the technical graph and stop. Database foreign keys, API call graphs, service dependencies - all good, all necessary, all insufficient.
The technical graph misses the organizational and contractual graph almost every time, because those dependencies don't live in code. They live in a Notion doc from a sales engineer, a Zapier integration a customer built without telling anyone, a CSV export a customer's ops team scheduled into their own warehouse three years ago and forgot about until it breaks.
I've seen a team do a genuinely rigorous static analysis - traced every function call, every API consumer, every internal service - declare the feature clean, deprecate it, and get blindsided four days later by a partner's automated system that had been scraping a public-facing page the feature rendered, because the partner had built their own scraper instead of waiting for an API three years ago and nobody at either company remembered.
The technical graph was accurate. It just wasn't the whole graph. The failure mode isn't "we didn't check." It's "we checked the wrong layer and mistook thoroughness in one dimension for completeness."
What this changes about how you should run deprecation
The practical implication is that "blast radius" needs to be assembled from at least three sources before a deprecation date goes on a roadmap:
- The technical dependency graph (services, APIs, DB schema, cron jobs)
- The contractual/commercial graph (SOWs, sales-promised capabilities, anything in a CRM note that references the feature by a customer's name for it, which is often not your internal name for it)
- The tribal graph (support tickets, Slack mentions, "who on this team would scream if this went away" - usually answerable only by asking, not querying)
Ruthless pruning of zombie features is correct as a strategy. Product bloat is real, maintenance debt compounds, and most orgs are too sentimental about sunk cost to kill anything on schedule. The failure isn't in the ambition to prune. It's in using adoption as a proxy for safety-to-remove, when adoption and dependency are measuring two different graphs that only sometimes overlap.
If you're heading into a deprecation cycle and want a structured way to actually build that three-layer map before you commit a kill date - something more repeatable than tribal knowledge and grep - I put together a working template for this exact process: https://dasdorf.gumroad.com/l/kcxacu
The goal isn't to save every zombie feature. It's to make sure the ones you kill stay dead for the reason you think, not because you found out about a $380k dependency after the fact.
Top comments
This is a useful way to frame it. The number I would want beside MAU is replacement cost under time pressure, because a low-use feature that only breaks at quarter close is still a very expensive option to be short.
Comments
No comments yet. Start the discussion.