How to Sunset a Legacy Feature Without Breaking Power Users
For: A product lead at a 5–50 person B2B SaaS company who has inherited a legacy feature that blocks a core architectural change — it has low aggregate usage but three enterprise customers have it wired into their workflows and will churn if it disappears without a migration path
The right way to sunset a legacy feature that a small cohort of enterprise customers depends on: freeze new adoption immediately, instrument the feature to discover the undocumented integrations your team doesn't know about, negotiate migration commitments with each affected account before you announce a public timeline, and only then start deleting code. Aggregate usage is a trap — a feature used by 2% of accounts can be load-bearing for a disproportionate share of revenue, and the real blocker is almost always the downstream integration built on top of the feature, not the feature itself.
This playbook is for a product lead at a 5–50 person B2B SaaS company who has inherited a feature that blocks a core architectural change (a schema migration, a new billing model, a platform rewrite), where usage looks low in aggregate but three or more enterprise accounts have it wired into their workflow. Customer success has two people. Engineering wants it gone yesterday. If you get this wrong, you either torch six-figure contracts or you never ship the architectural change.
Step 1: Stop the bleeding — freeze new adoption before you announce anything
Before you tell a single customer, before you write a deprecation notice, before you even loop in CS, gate the feature behind a flag that prevents new accounts from touching it. This is not the deprecation announcement. This is the tourniquet.
Every day the feature stays open to new users, your migration surface area grows. New customers wire it up, integrate it, build reports on it. If your sunset takes two quarters, that's two quarters of new dependencies accreting on a feature you're trying to kill.
Concretely: add a feature flag or entitlement check that limits access to the current set of accounts using it in the last 60–90 days. Do not announce this. New signups simply won't see it. Existing users see no change.
Anti-pattern: Skipping this because "usage is already low, it won't matter." Low usage compounds. Every new dependency you allow now is one you have to migrate later.
You'll know this step is done when: new account creation cannot activate the feature, and you have a stable, finite list of accounts you actually have to worry about.
Step 2: Instrument the feature harder than it's ever been instrumented
This is where most sunset plans die. You look at your existing analytics — MAU on the feature, click events, API calls — and think you understand usage. You don't. You understand the surface. What you need to find is the downstream integration nobody on your team has ever seen.
The pattern I see over and over: a feature exports data on a schedule. Two customers built internal dashboards that scrape that export. Their finance team runs a monthly close off it. Nobody at your company knows this exists. When you deprecate the export, their close breaks, and you find out on a Tuesday from a very angry email.
What to instrument:
- API call patterns by account — not just volume, but timing (is it a cron job at 2am on the 1st of every month? That's an accounting integration).
- User agents and IPs hitting the endpoints. Non-browser user agents mean scripts. Scripts mean integrations you don't know about.
- Data egress — is the output being exported, webhooked, or synced elsewhere?
- Which internal users at the customer touch it — if it's the CFO's assistant, you have a finance dependency. If it's an engineer's service account, you have an integration.
Run this for at least a full monthly and quarterly cycle. Features tied to accounting close, quarterly reporting, or invoicing look dormant for 27 days and then critical for 3.
Anti-pattern: Trusting your existing product analytics. Segment or Amplitude events were instrumented for growth, not deprecation. They almost never capture the shape of an integration dependency.
You'll know this step is done when: for every account still using the feature, you can describe in one sentence what business process the feature is embedded in — not just that it's used.
Step 3: Segment the affected accounts by migration complexity, not by revenue
Once you know the shape of each customer's dependency, sort them into three buckets. Do this before you talk to anyone.
- Trivial migrations. They use the feature manually, in the UI, occasionally. You can point them at an existing alternative in the product and they'll be fine. Usually 50–70% of the affected accounts.
- Documented integrations. They use the API, but the integration is visible to you (via your own SDK, official webhook, published integration). You know what would break and you can design a compatible replacement.
- Undocumented, load-bearing integrations. The three enterprise accounts you were worried about. Custom scripts, scraped exports, embedded workflows. Migration requires their engineering time, not just yours.
The bucket-3 accounts are your entire problem. Buckets 1 and 2 will migrate with a decent email and updated docs. Bucket 3 will churn if you handle them like bucket 1.
Anti-pattern: Prioritizing by ARR. A $200K account with a trivial migration is a footnote. A $60K account with a load-bearing custom integration will eat your quarter. Sort by migration complexity first, then by revenue within each bucket.
You'll know this step is done when: you have a spreadsheet of every affected account, their bucket, and a one-line description of what would break if the feature disappeared tomorrow.
Step 4: Talk to bucket 3 accounts one-on-one — before you publish a timeline
This is the step everyone wants to skip because it's slow and unscalable. Skip it and you'll pay for it in churn.
For each bucket-3 account, book a 30-minute call with the person who actually uses the feature (not the buyer, not the executive sponsor — the operator). Do not frame it as "we're killing this feature." Frame it as: "We're planning changes to X. Can you walk me through how you use it today so we don't break anything?"
You are gathering three things on this call:
- The full shape of the dependency, including anything your instrumentation missed.
- Their internal migration cost — do they have engineering bandwidth, or is this a Google Sheet held together by one analyst who's leaving in three months?
- What a replacement would need to do for them to be willing to migrate.
Half the time, you'll discover the "critical" dependency is actually a workaround for something you fixed two years ago and they never noticed. The other half, you'll discover a genuine gap that your replacement path has to fill.
Do not commit to a sunset date on these calls. Say "we're planning changes and want to make sure you have a smooth migration path — we'll come back to you with a specific timeline once we've talked to everyone."
Anti-pattern: Sending a survey instead of doing calls. Surveys give you the version of the workflow the customer thinks you want to hear. Calls give you the version where they admit their intern maintains it with a Python script.
You'll know this step is done when: for each bucket-3 account, you can name (a) the specific person whose workflow will break, (b) what they'd need in a replacement, and (c) whether their team has engineering capacity to migrate.
Step 5: Build the migration path before you announce the sunset
Never announce a deprecation without a working alternative already shipped. The gap between "we're removing this" and "here's what to use instead" is where trust dies.
The migration path can take several shapes:
- A new feature that supersedes the old one. Preferred, but expensive.
- Repointing to an existing feature. Cheap when it works.
- An export/import bridge. A one-time or scheduled tool that gets the customer's data out of the old model and into whatever they use next.
- A documented API alternative. Works for bucket-2 accounts, often not enough for bucket-3.
For bucket-3 accounts, the replacement often needs to include something you'd never ship publicly — a compatibility shim, a legacy endpoint that stays alive for those accounts specifically, or a scheduled export in the old format. That's fine. Contain the ugly to the smallest possible surface.
If the architectural change you're trying to unblock is significant — a database migration, a rewrite, a platform consolidation — this is where an external partner can compress the timeline. We've done this pattern with clients modernizing older systems; see how we approach legacy modernization for context on scoping this kind of work.
Anti-pattern: Announcing the sunset and promising the migration tool is "coming soon." You will slip. Customers will notice. Trust erodes and then compounds against every subsequent announcement.
You'll know this step is done when: a bucket-3 customer could migrate today, using tools that exist in production, without any custom work from your team.
Step 6: Announce with a real timeline, tiered by bucket
Now you can announce. Not before. The announcement has three components:
- A public deprecation notice with a hard end-of-life date, sent to every affected account. This is your bucket-1 and bucket-2 communication. It links to migration docs and the replacement path.
- Direct outreach to bucket-3 accounts from their CSM or account owner, with a proposed migration plan specific to their integration. This should not be their first time hearing about it — you've already talked to them in step 4.
- An internal escalation path. When (not if) a bucket-3 customer pushes back, who decides whether to grant an extension? Decide this before the first request comes in.
On timelines: the deprecation window should be long enough that a customer's engineering team can slot the migration into a normal sprint cycle, not so long that the project loses urgency internally. Bucket-3 accounts often need longer than bucket-1. That's fine — extend the end-of-life for specific accounts, don't extend it for everyone.
Anti-pattern: Announcing to everyone simultaneously with a generic email. Your bucket-3 CFO should not learn her month-end close is breaking from the same email that goes to a customer who used the feature twice in 2022.
You'll know this step is done when: every affected account has (a) received a communication appropriate to their bucket, (b) acknowledged receipt, and (c) has a named contact on your side.
Step 7: Enforce the end-of-life — but keep a compatibility shim for the last holdouts
The last 5% of migration is the hardest. Some bucket-3 accounts will drag their feet until the day of the end-of-life date, then request an extension. Grant it once, with a new hard date. Don't grant it twice.
What actually works: after the public end-of-life, the feature is removed from the UI and default API surface, but a narrow compatibility endpoint remains alive for a small named list of accounts, for a bounded additional window. This buys you the architectural change (you can now do the schema migration, retire the code paths, etc.) while giving the last holdouts a runway.
Document the shim's own end-of-life. Set a calendar reminder. Kill it when the date comes.
Anti-pattern: Leaving the shim alive indefinitely because "no one's complained." That shim is now a permanent feature that will show up in the next architectural review, and you'll be reading this playbook again in two years.
You'll know this step is done when: the code path is deleted, the shim has a documented sunset date, and the architectural change you were trying to unblock is in production.
Failure modes I've seen
The "low usage means low risk" fallacy. A team looks at a dashboard, sees 1.8% of accounts use a feature, ships a deprecation email, and loses two enterprise contracts. Usage metrics are the wrong denominator. Revenue-weighted, integration-weighted usage is the right one.
The CS bottleneck. You've done everything right, but customer success has two people and 40 accounts to hand-hold. Migrations stall. The sunset slips two quarters. Fix: get engineering to build self-serve migration tools for bucket 1 and 2 so CS can focus entirely on bucket 3.
The internal champion who moved on. The person at the customer who built the integration left six months ago. Nobody at the customer knows how it works. When it breaks, their team blames you. Mitigation: in your step 4 calls, always ask "who else knows how this works?" If the answer is nobody, treat the migration as higher risk, not lower.
The scope creep replacement. The replacement feature grows to cover every edge case anyone mentioned, ships six months late, and no longer justifies the architectural change that started this whole exercise. Fix: the replacement covers the documented workflows of the affected accounts. Not more.
The reversal. Halfway through the sunset, an executive gets a call from a bucket-3 customer and rolls back the deprecation. The feature stays. Engineering morale craters. Fix: get the executive sponsor bought in during step 3, not step 6. If they're not committed to the sunset when it's still theoretical, they will fold when it becomes real.
The forgotten shim. Two years later, a new engineer discovers a legacy endpoint keeping three accounts alive. Nobody remembers why. The shim now blocks the next architectural change. Document sunset dates for your workarounds the same way you document sunset dates for the features they replace.
Frequently Asked Questions
How long should a feature deprecation window be for enterprise SaaS customers?
Long enough that the affected customer's engineering team can slot the migration into a normal planning cycle — typically at least one full quarter for customers with integrations, longer if the integration is load-bearing for financial or compliance workflows. Bucket-3 accounts (undocumented, deep integrations) often need more runway than bucket-1 accounts. Set different end-of-life dates per bucket rather than one date for everyone.
Should I offer a discount or credit to customers affected by a feature sunset?
Usually no. Discounts frame the sunset as damage that needs compensation and set an expectation that every future deprecation triggers a negotiation. What works better: invest that budget into migration tooling and one-on-one migration support for the highest-risk accounts. Customers care more about not having their workflow break than about a credit on next month's invoice.
What if a customer refuses to migrate and threatens to churn?
First, verify the threat is real by understanding what specifically breaks for them — often the "we'll churn" position softens once you show a concrete migration path. If it's genuine and the account is material, grant a bounded extension with a compatibility shim, not an indefinite reprieve. If the account is small and the threat is disproportionate to their value, accept the churn — one difficult account is not worth blocking an architectural change that benefits the rest of your customer base.
How do we discover undocumented customer integrations before we deprecate?
Instrument the feature at the API and data-egress level, not just the UI level. Look for non-browser user agents, scheduled call patterns (especially around month-end, quarter-end), and consistent data exports. Then do 30-minute calls with the actual operators at each account — not the buyers. The person who built the integration will tell you exactly how it works if you ask them to walk you through their workflow instead of asking about the feature directly.
Who should own the feature sunset — product, engineering, or customer success?
Product owns the decision and the timeline. Engineering owns the replacement and the code removal. Customer success owns the account-by-account migration for bucket-3 customers, with product providing the migration tooling and engineering providing escalation support. If any one function tries to own the whole sunset alone, it stalls — CS burns out on bucket 3, engineering ships a replacement nobody uses, or product announces a timeline the other two functions can't hit.
Found this useful? CodeNicely publishes engineering and product playbooks weekly. Browse the archive or tell us what you're building.
_1751731246795-BygAaJJK.png)