System Design Interview: Design a Loyalty and Rewards Points System

What Is a Loyalty and Rewards System?

A loyalty program tracks user activity and awards points redeemable for discounts or products. Amazon Prime, Starbucks Rewards, and airline miles programs are examples. Core challenges: points accrual without race conditions, expiry, transaction consistency, and scaling to tens of millions of members.

  • Coinbase Interview Guide
  • LinkedIn Interview Guide
  • Airbnb Interview Guide
  • DoorDash Interview Guide
  • Stripe Interview Guide
  • Shopify Interview Guide
  • System Requirements

    Functional

    • Award points for purchases, referrals, reviews, login streaks
    • Redeem points against purchases
    • Points expiry after 12 months
    • Tier system: Bronze/Silver/Gold based on spend
    • Promotional multipliers: 2x/3x points during campaigns

    Non-Functional

    • Exact consistency — no lost or duplicated points (billing depends on it)
    • Idempotency — retries must not double-award
    • 100M members, 10K transactions/second peak

    Core Data Model

    members: id, user_id, tier, total_earned, total_redeemed
    point_transactions: id, member_id, type(earn/redeem/expire), amount,
                        reference_id, reference_type, created_at, expires_at
    point_balances: member_id, available_points, tier_points_ytd
    

    Points Accrual — Idempotency

    Risk: loyalty service crashes after processing but before committing Kafka offset — event redelivered, double points. Solution: unique constraint on (reference_id, reference_type):

    INSERT INTO point_transactions
        (member_id, type, amount, reference_id, reference_type)
    VALUES (?, 'earn', ?, 'order_123', 'purchase')
    ON CONFLICT (reference_id, reference_type) DO NOTHING;
    

    Even if processed twice, only one row is inserted. Safe to replay Kafka events.

    Balance — Ledger + Cached Sum

    Update cached balance atomically with each transaction:

    BEGIN TRANSACTION;
    INSERT INTO point_transactions (...) VALUES (...);
    UPDATE point_balances
      SET available_points = available_points + ?
      WHERE member_id = ?;
    COMMIT;
    

    Redemption — Preventing Overdraft

    User tries to redeem 500 points but only has 300. Pessimistic locking:

    BEGIN TRANSACTION;
    SELECT available_points FROM point_balances
    WHERE member_id = ? FOR UPDATE;  -- row lock
    IF available_points < 500: ROLLBACK;
    INSERT INTO point_transactions (type='redeem', amount=-500, ...);
    UPDATE point_balances SET available_points = available_points - 500;
    COMMIT;
    

    FOR UPDATE prevents two concurrent redemptions from both succeeding with insufficient balance. Low contention (one user’s balance) justifies pessimistic over optimistic locking here.

    Points Expiry

    • Store expires_at on each earn transaction
    • Nightly batch job: compute unredeemed amount of expiring transactions, insert expiry transaction
    • FIFO redemption: consume oldest non-expired points first
    • Notify members 30/7/1 days before expiry

    Tier System and Promotions

    Tiers computed by nightly job on rolling-12-month spend. Store tier_points_ytd in point_balances. Promotions: at accrual time, check active promotions and apply highest multiplier. Store multiplier in point_transactions for auditing.

    Caching

    • Member balance/tier: Redis 30-second TTL, invalidated on each transaction
    • Active promotions: Redis 5-minute TTL
    • Transaction history: paginated, 60-second TTL

    Interview Tips

    • Idempotency via unique constraint on reference_id is the core reliability pattern.
    • Ledger + cached balance = double-entry accounting — shows financial systems knowledge.
    • FOR UPDATE for redemption: low contention but high cost of overdraft justifies pessimistic locking.
    • FIFO expiry and tier computation are product details that differentiate senior candidates.
    Scroll to Top