Requirements
- Earn points on purchases and specific actions (sign-up, referral, review)
- Redeem points for discounts, rewards, or free items
- Tiered membership (Bronze, Silver, Gold, Platinum) with benefits per tier
- Points expiry: points expire if unused for 12 months
- 100M members, 10M transactions/day, <100ms point balance lookup
Data Model
LoyaltyAccount(account_id UUID, user_id UUID, tier ENUM(BRONZE,SILVER,GOLD,PLATINUM),
points_balance BIGINT, lifetime_points BIGINT,
tier_qualifying_points INT, -- points earned in current tier year
tier_expiry_date DATE, created_at)
PointTransaction(txn_id UUID, account_id UUID, type ENUM(EARN,REDEEM,EXPIRE,ADJUST),
points BIGINT, reference_id UUID, reference_type VARCHAR,
description, expires_at DATE NULL, created_at)
-- All point changes go through transactions — never update balance directly
PointExpiry(expiry_id UUID, account_id UUID, txn_id UUID,
points BIGINT, expires_at DATE, status ENUM(ACTIVE,EXPIRED,REDEEMED))
Earning Points
Earning rules are configurable: 1 point per $1 spent, 3x points for a specific product category, 500 bonus points for referral, etc. Rules stored in a EarnRule table and evaluated at transaction time. The earn logic:
- Order completed → publish OrderCompleted event to Kafka
- Loyalty service consumes event, evaluates earn rules
- Calculate points = base_earn_rate * amount + bonus_points for applicable rules
- INSERT PointTransaction (type=EARN, points=calculated)
- UPDATE LoyaltyAccount SET points_balance += points, lifetime_points += points, tier_qualifying_points += points
- Evaluate tier upgrade: if tier_qualifying_points >= tier threshold, upgrade tier
Redeeming Points
def redeem_points(account_id, points_to_redeem, order_id):
BEGIN TRANSACTION
SELECT points_balance FROM LoyaltyAccount
WHERE account_id = :id FOR UPDATE
if points_balance < points_to_redeem:
ROLLBACK; raise InsufficientPoints
# Deduct using FIFO from expiring points first (redeem oldest first)
deduct_from_expiry_buckets(account_id, points_to_redeem)
UPDATE LoyaltyAccount SET points_balance -= points_to_redeem
INSERT PointTransaction (type=REDEEM, points=-points_to_redeem, reference_id=order_id)
COMMIT
return calculate_discount(points_to_redeem)
Redemption rate example: 100 points = $1 discount. Deduct from expiry buckets in FIFO order — use points expiring soonest first. This reduces the number of points that expire unused.
Points Expiry
Points earned in each transaction expire after 12 months of account inactivity or on a rolling 12-month basis per earn event. Implementation: PointExpiry table tracks each earn event’s expiry date. A nightly batch job runs:
SELECT account_id, SUM(points) as expiring_points FROM PointExpiry WHERE expires_at = CURRENT_DATE AND status = 'ACTIVE' GROUP BY account_id LIMIT 10000 -- process in batches
For each account: INSERT PointTransaction (type=EXPIRE, points=-expiring_points), UPDATE LoyaltyAccount balance, mark PointExpiry records as EXPIRED. Send email notification 30 days before expiry: “You have X points expiring on DATE.”
Tier Management
Tier qualification period: typically calendar year or rolling 12 months. Tier thresholds: Bronze=0, Silver=1000 QP (qualifying points), Gold=5000 QP, Platinum=10000 QP. Tier upgrade: immediate when threshold is crossed. Tier downgrade: at tier year end, re-evaluate based on prior-year QP. Member retains previous tier for a grace period (e.g., 3 months) after year end. Cache tier status in Redis for fast benefit lookups: key=loyalty_tier:{user_id}, TTL=1h.
Key Design Decisions
- Double-entry bookkeeping via PointTransaction — never modify balance directly, full audit trail
- SELECT FOR UPDATE on redemption — prevents concurrent over-redemption
- PointExpiry table for FIFO redemption — use oldest points first, minimize waste
- Async earn via Kafka — order completion never blocks on loyalty points calculation
- Nightly batch for expiry — avoid real-time TTL complexity, predictable processing window
Shopify system design covers loyalty programs and customer rewards. See common questions for Shopify interview: loyalty program and rewards system design.
Airbnb system design covers loyalty programs and host/guest rewards. Review patterns for Airbnb interview: loyalty and rewards system design.
Lyft system design covers loyalty programs and driver/rider rewards. See design patterns for Lyft interview: loyalty program and rewards system design.