Pricing in Practice/

Adding Pricing Tiers After Launch: What to Take Stock of Before You Split a Plan

Your product already has users on one plan, and now it needs tiers or add-ons. The four places a tier lives, where existing customers can land, and what it costs to stop naming plans in your code.

6 min read


The product works and people use it. There is one plan, or none. Now you want to split it: a cheaper tier, a team tier, a few features moved into paid add-ons.

On paper this looks like a configuration change. Draw a table of plans and features, create some prices, and you are done. One founder described reaching this point with an application that already had customers, jobs, scheduling, inventory, user roles, permissions and reports, and finding the split into "included in paid" and "add-on modules" much harder than it had looked.

The reason is that a plan is not stored in one place. Before you change it, find every place it lives.

Take stock: four places a tier lives

1. The price catalog at your payment provider. This is the part that looks like configuration, and it has rules of its own. Stripe's documentation on managing prices states that once a price is created, its amount cannot be changed; you can edit its metadata, nickname and active flag. To change a price, you create a new one, switch to its ID and archive the old one. An archived price cannot be added to new invoices or subscriptions, but subscriptions already on it stay active until they are cancelled. A tier can also be several prices: monthly and yearly, or one per currency.

2. Features and permissions. Which plan turns which feature on. If you keep this at the payment provider, Stripe's entitlements feature lets you define features and attach them to products. Its documentation states that when you attach a new feature to a product, existing subscriptions get the matching entitlement at the start of their next billing period. It also recommends storing entitlements in your own database so your application can check them without a call to the provider, which means you hold a copy that has to stay in step.

3. Limits and counters. How many projects, seats, reports or API calls each plan allows, and what counts toward them. Limits usually sit next to the data they limit, and existing customers may already be past the line a new tier would draw. A customer with fifty projects, moving to a tier that allows ten, needs a decision before the change, not an error after it.

4. Plan checks in your code. Every if (user.plan === "pro") or isPaidUser. These are the easiest to add and the hardest to find later. They also grow: a check for "pro" becomes "pro or team", then "pro or team or the customer with the custom contract".

For each feature you are moving, note which of the four it depends on. They change at different times: the catalog when you edit it, entitlements at the provider at the next billing period for existing subscriptions, and your code and stored limits when you deploy. Knowing which of the four each feature touches is most of the plan.

Where existing customers land

Every existing customer has to end up somewhere. Three approaches are common.

Move them automatically. The founder of a blog-hosting service described replacing per-blog pricing with three tiers and moving existing subscriptions over automatically, with no choice required. A code-hosting platform, when it lowered the price of its team plan, announced that existing customers' bills would be reduced automatically. In both cases nobody paid more for what they already had. Moving customers automatically onto a higher price is a different conversation.

Keep them on what they bought. This is the default at the payment provider: an archived price keeps renewing for subscriptions already on it. One developer who rebuilt pricing for his own product versioned his pricing so that each account stays on the version it signed up under, because he did not want to force anyone who was happy to change plans. The cost, as he describes it, is that old and new pricing run side by side and both have to be maintained and tested. Our post on repricing existing customers covers that trade-off in more detail.

Let them choose. Offer the old plan and the new tiers side by side and let each customer move when they like. This respects the customer's choice and costs the most to explain: everyone has to understand both options, and you support both until the last customer moves.

Whichever you pick, decide it before the data decides for you. Customers whose usage already crosses a new boundary, with more seats or projects than their new tier allows, need an explicit answer before the change goes out.

Name the feature, not the plan

The way to make the next change smaller is to stop asking "which plan is this customer on?" and start asking "does this customer have this feature?" The plan becomes a list of what it grants, and your code names features. Checking a feature before letting a customer use it is often called feature gating.

Pete Hodgson's article on feature toggles describes this kind of toggle as a permissioning toggle: it changes which features some users get, it can live for a long time, and the decision is made on every request. The article recommends putting toggle decisions behind one central object, so that changing the logic means changing one place, and treating toggles as inventory with a carrying cost, since they multiply.

The developer mentioned above built the same split into his data model. Pricing (prices, billing periods, which plans are on sale) is kept separate from entitlements (on-or-off features and numeric limits), each is versioned on its own, and the code asks whether an account has a feature rather than which plan it is on. He also lists the costs: supporting several pricing versions was more than his product needed apart from honouring existing arrangements, old seat-based pricing had to run alongside the new flat prices, and every toggle and pricing version needed tests. He adds that the approach may not be practical for a new product or for most teams.

That is the trade-off in general. Naming features adds a layer: one more table or configuration, one more lookup on each request, and a list of features someone has to keep tidy. What you get back is that moving a feature between plans no longer means changing and deploying code.

Our documentation walks through one feature, two plans and one check that names the feature rather than the plan: Gate a feature by plan.

Where that layer can live

Whatever answers "does this customer have this feature?" has to live somewhere, and each place has its own costs.

  • In your own code. A small module maps plans to features and limits and answers the question. You control it, and it is one more thing you maintain and test.
  • In a feature-flag service. One such service's documentation describes using long-lived flags, targeted at groups of customers, to manage entitlements. It also lists the risks: if your application cannot reach the service, users get the SDK's fallback value, which may give them more or less than they paid for; long-lived flags can run unattended until their defaults and fallbacks are out of date; and a flag may outlast the person who created it, so its name has to explain what it is for.
  • At the payment provider. Stripe's entitlements change with the subscription, with the timing described above for new features. Its documentation adds that the summary event sent when entitlements change lists at most ten of them, with the rest fetched separately, and it still suggests keeping a local copy.

The change after this one

Splitting a plan once is not the end. The next price change, the next add-on, or a customer who asks for seats instead of usage starts the same work again. One person at a company of about 35 people, selling to a large number of low-priced customers, described each price change as a project of its own, with custom deals the hardest part, and said that changes they could not automate did not get done. Another founder described having little view of the impact before a change, and estimating it by exporting subscriptions into a spreadsheet.

A price change is hard to take back once customers have seen it. Before you make one:

  • For each feature you are moving, list which of the four places it depends on.
  • For every existing customer, know where they land and whether they will pay more.
  • Find the customers already past the new limits.
  • Count the plan checks in your code. If you are about to add more, consider naming the feature instead.

This post was written by the team building ShipRev, which keeps a SaaS product's identity, subscriptions, metering and entitlements on one customer record. ShipRev is not open yet: the managed cloud is built but not yet open, and the source repository is not public.

Related use case: Plan limits

Keep reading