Subscription Plan Design for SaaS for Technical Founders

Subscription plan design is where product strategy and system architecture intersect. For technical founders, getting the architecture wrong early is expensive — a plan structure that made sense at 50 customers becomes a database migration nightmare at 5,000. Getting the pricing strategy wrong is equally expensive but in the opposite direction: ship too few tiers and you leave upgrade revenue on the table; ship too many and you confuse prospects and stall conversion.

This guide treats subscription plan design as an engineering problem with a business constraint. It covers how to model subscription tiers architecturally, which features to gate and why, how to implement usage limits without breaking your system under load, and how to build an entitlement layer that evolves without constant rewrites.

🏗️ Four Pricing Model Architectures

Before you write a line of entitlement code, you need to choose a pricing model. The choice determines your data model, your billing infrastructure, and how complex your upgrade logic will be.

ModelHow Billing WorksArchitectural ComplexityBest Fit
Flat RateFixed monthly fee per accountLow — one price, one entitlement setSimple tools, narrow use cases
Tiered PlansFixed fee per tier (Starter / Growth / Pro)Medium — plan-level feature gatesMost B2B SaaS at seed to Series A
Usage-BasedPer unit consumed (API calls, seats, GB)High — metering, aggregation, overage logicInfrastructure, AI/ML, developer APIs
HybridBase subscription plus usage chargesHigh — combines tier gates and meteringMature products with diverse usage patterns

Flat Rate

Flat rate is the simplest to implement and the easiest for customers to understand. Entitlement logic is binary: the customer either has access to everything or they do not. The main architectural benefit is that you can skip the entitlement layer entirely at the start and use a single boolean check against subscription status.

The problem with flat rate is it limits your revenue ceiling. There is no upgrade path short of a price increase, and large customers paying the same as small customers creates LTV compression. Most flat-rate products eventually migrate to tiered — plan for that migration early.

Tiered Plans

Tiered plans (typically three tiers) are the most common structure for B2B SaaS. Each tier is a bundle of features and limits. Architecturally, tiers require a plan-to-feature mapping and a way to evaluate that mapping at request time. This is the entitlement layer.

The key design principle: tiers should be defined in data, not in code. Hardcoding if plan === 'pro' conditions throughout your codebase is what creates the migration nightmare. Instead, define plan capabilities in a configuration object or database table, and evaluate them through a single entitlement service.

Usage-Based Pricing

Usage-based pricing (UBP) charges customers based on consumption. This requires a metering system: you must accurately count events, aggregate them over billing periods, and pass totals to your billing provider. The architecture is materially more complex than tiered plans because every usage event must be captured, stored, and reconciled with billing.

UBP also creates collection risk: customers who consume unexpectedly large amounts may receive invoice shock, leading to disputes and churn. Implement usage alerts and spending caps before you launch UBP to any significant customer base.

Feature Gating vs. Usage Gating

Two distinct gating mechanisms exist in subscription plan design, and they require different implementations.

Feature gating controls whether a capability is available at all. A customer on the Starter plan cannot access the API. A customer on the Growth plan can. The gate is binary: yes or no. Technically, this is a lookup against the entitlement table for the customer's current plan.

Usage gating controls how much of a capability a customer can consume. A customer on Growth can make 1,000 API calls per month. After 1,000, requests are either blocked (hard limit) or charged at an overage rate (soft limit). Technically, this requires a usage counter maintained at the account level, updated on every qualifying event, and checked against the plan's limit before serving the request.

Gate TypeImplementationDatabase PatternFailure Mode to Guard
Feature gateEntitlement lookup per requestplan_features join tableCache staleness after plan change
Usage gate (hard)Counter check before actionusage_counters, reset on billing cycleRace conditions on concurrent requests
Usage gate (soft)Counter track + overage calculationusage_events append log + aggregationAggregation lag causing billing errors
Seat gateActive member count vs. plan limitteam_members count vs. plan.max_seatsStale count cache during invite flow

Handling Race Conditions on Usage Limits

The classic failure mode for usage gating is the race condition: two concurrent requests both read the counter at 999 (limit: 1,000), both determine they are within the limit, both execute, and the counter ends at 1,001. For low-stakes limits this may be acceptable. For billing-critical limits, use atomic increment operations (Redis INCR or database row-level locking) and check the returned post-increment value against the limit.

Designing an Entitlement System

The entitlement system is the layer that answers: given the current account and its active subscription, what is this account allowed to do? A well-designed entitlement system has three properties: it is fast to evaluate, it is easy to change, and it is decoupled from billing logic.

Schema Pattern

A minimal entitlement schema has four components:

The override table is critical. Every SaaS product eventually needs to give a specific customer access to a feature outside their plan — for a pilot, a beta program, or a negotiated deal. Without the override table, you end up hardcoding exceptions in the application layer.

Entitlement Resolution Order

When resolving whether an account can use a feature, evaluate in this order:

  1. Check account-level overrides first — these take precedence over plan rules
  2. Check the account's active subscription plan against the plan_features table
  3. If no match, deny access (fail closed)

Fail closed is the correct default. If your entitlement system encounters an unexpected state — no active subscription, missing plan data — it should deny access, not grant it. Granting on uncertainty creates unpredictable free access.

Caching Strategy

Entitlement checks can happen dozens of times per request. A cold database query per check will kill your p99 latency. Cache the resolved entitlement set for each account with a short TTL (60-300 seconds). On plan change events, invalidate the cache immediately. Use a cache-aside pattern: check cache first, fall through to database on miss, populate cache on return.

Upgrade Logic and Instrumentation

Upgrade logic is the set of rules and signals that determine when to show a customer an upgrade prompt. Technically, this means evaluating whether a customer has hit a limit or attempted to use a gated feature, and surfacing that moment in the UI.

Upgrade Trigger Points

The highest-converting upgrade prompts appear at the exact moment a customer tries to do something they cannot. The technical implementation:

Returning structured denial reasons rather than generic 403 errors is the architectural decision that enables contextual upgrade prompts. If your API returns 403 Forbidden with no body, your frontend has to guess why the request failed and cannot show a useful upgrade prompt.

Instrumentation

Track these events for every plan-related action:

EventProperties to CaptureUsed For
feature_gate_blockedaccount_id, feature_key, current_planIdentifying which features drive upgrades
usage_limit_reachedaccount_id, metric, current_value, limitIdentifying which limits cause friction
upgrade_prompt_shownaccount_id, trigger, from_plan, to_planMeasuring upgrade prompt effectiveness
plan_upgradedaccount_id, from_plan, to_plan, triggerAttribution of upgrades to trigger points

The trigger property on plan_upgraded closes the loop: it tells you which specific feature gate or usage limit was the last thing the customer hit before upgrading. This data tells you which gates are actually driving revenue, which lets you make data-driven decisions about where to move the gates on future pricing iterations.

Plan Architecture Mistakes to Avoid

Several common architectural decisions in subscription plan design cause significant rework later.

Hardcoding Plan Names in Application Logic

Checking if account.plan === 'starter' directly in application code couples your business logic to specific plan names. When you rename, restructure, or add plans — which you will — every hardcoded check becomes a potential bug. Always go through the entitlement system, never check plan names directly.

Binding Entitlements to Billing Events

Entitlement changes (what the account can access) and billing events (what charge was processed) are related but not identical. An account can be on a paid plan but in a grace period, on a trial, or have a manually extended access window. If you tie entitlements directly to payment success webhooks, you lose the flexibility to handle these edge cases. Model subscription state separately from payment state.

Not Planning for Grandfathering

When you change plan pricing or feature sets, existing customers often need to stay on their current terms for a period. This requires a mechanism for accounts to be on a plan version that no longer exists in your current plan catalog. The solution: store the plan configuration (feature set and limits) at subscription creation time, or maintain versioned plan records. Do not assume you can always read current plan config and apply it to all subscriptions.

Frequently Asked Questions