Low Level Design: Plan Management Service

What Is a Plan Management Service?

A Plan Management Service owns the catalog of subscription plans and the logic for upgrading, downgrading, and switching between them. It is a supporting service to the subscription lifecycle: while the subscription service tracks which plan a customer is on, the plan management service defines what plans exist, what they cost, what features they unlock, and how mid-cycle plan changes are prorated.

Data Model

-- Plan catalog
CREATE TABLE plans (
    plan_id        INT PRIMARY KEY AUTO_INCREMENT,
    code           VARCHAR(50) NOT NULL UNIQUE,   -- e.g. 'pro_monthly'
    display_name   VARCHAR(100) NOT NULL,
    price_cents    INT NOT NULL,
    currency       CHAR(3) NOT NULL DEFAULT 'USD',
    interval       ENUM('monthly','annual') NOT NULL,
    trial_days     INT NOT NULL DEFAULT 0,
    is_public      BOOLEAN NOT NULL DEFAULT TRUE,
    created_at     TIMESTAMP NOT NULL DEFAULT NOW()
);

-- Feature entitlements per plan
CREATE TABLE plan_features (
    plan_id        INT NOT NULL REFERENCES plans(plan_id),
    feature_key    VARCHAR(100) NOT NULL,
    feature_value  VARCHAR(255) NOT NULL,   -- e.g. 'seats=10', 'storage_gb=100'
    PRIMARY KEY (plan_id, feature_key)
);

-- Change history (proration ledger)
CREATE TABLE plan_changes (
    change_id         BIGINT PRIMARY KEY AUTO_INCREMENT,
    subscription_id   BIGINT NOT NULL,
    old_plan_id       INT NOT NULL,
    new_plan_id       INT NOT NULL,
    effective_at      TIMESTAMP NOT NULL,
    proration_cents   INT NOT NULL DEFAULT 0,   -- positive = credit, negative = charge
    reason            ENUM('upgrade','downgrade','admin') NOT NULL,
    created_at        TIMESTAMP NOT NULL DEFAULT NOW()
);

Core Algorithm: Proration

When a customer switches plans mid-cycle, the service must calculate a prorated credit or charge. The standard algorithm:

  1. Compute days remaining in the current period: days_left = (period_end - NOW()) / 86400.
  2. Compute total days in the period: period_days = (period_end - period_start) / 86400.
  3. Credit for unused old plan: credit = old_price_cents * (days_left / period_days).
  4. Charge for new plan remainder: charge = new_price_cents * (days_left / period_days).
  5. Net proration: charge - credit. Positive = bill now. Negative = apply as account credit.

All arithmetic is done in integer cents with explicit rounding rules (round half-up) documented and tested. Never use floating-point for money.

Plan Change Workflow

  • Immediate upgrade: apply new plan now, charge proration immediately.
  • Downgrade at period end: set pending_plan_id on subscription; apply at renewal to avoid prorating a lower price mid-cycle.
  • Admin override: support zero-proration plan moves for support cases, recorded with reason='admin' and an actor user ID.

Failure Handling

Plan changes that include a charge must wrap the proration payment and the subscription update in a distributed saga: charge first, then update the subscription. If the payment fails, the plan change is rolled back and the customer remains on their current plan. Store plan_changes rows with a status column (pending, applied, failed) and process them transactionally.

Scalability Considerations

  • The plan catalog is small and read-heavy. Cache the full catalog in application memory, refreshed every 5 minutes. A single cache miss is acceptable; stampede protection is not needed at this scale.
  • Feature entitlement lookups (what can this user do?) should be served from the cache, not a per-request DB join.
  • Plan changes are infrequent relative to reads. A write to plan_changes plus a subscription update is the hot write path; a single writer per subscription (using optimistic locking on a version column) prevents concurrent change conflicts.

Summary

Plan Management is a catalog plus a proration calculator plus a change ledger. The catalog should be cached aggressively. Proration must be integer arithmetic with explicit rounding. Plan changes that involve money must use a saga pattern to stay consistent across the payment gateway and the subscription database. Separating immediate upgrades from deferred downgrades keeps billing surprises to a minimum.

See also: Stripe Interview Guide 2026: Process, Bug Bash Round, and Payment Systems

See also: Shopify Interview Guide

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

Scroll to Top