This is a classic question from Stripe's Billing team. It tests Event Scheduling, Timeline Sorting, and State Propagation.
Problem Description
Stripe needs to send email notifications to subscribers. Each user has a subscription plan, and emails need to be sent at specific points in their subscription lifecycle.
Part 1: Static Schedule
Given a Schedule Template and a list of User Subscriptions, output all email logs sorted by time.
Schedule Template:
Start: "Welcome email"T-15(15 days before end): "Upcoming expiry"End: "Subscription expired"
User Data:
- Alice: Start=0, Duration=30 days
- Bob: Start=10, Duration=30 days
Expected Output: Calculate the specific send dates (Day 0, Day 15, Day 30) for each user, merge all events, and print them sorted by timestamp.
Part 2: Dynamic Changes
Users might change their Plan mid-cycle (e.g., upgrading from Silver to Gold). New Rules:
- If a user changes their Plan, immediately send a "Plan Changed" email.
- All subsequent scheduled emails (like "Expired") must reflect the latest Plan name in the subject line.
Input Addition:
PlanChanges = [{user: "Alice", time: 5, new_plan: "Gold"}]
The Challenge: You need to dynamically update the content of Alice's pending emails at Day 15 and Day 30.
oavoservice Solution Analysis
This problem effectively maps to a Priority Queue or Event Merging & Sorting pattern.
1. Data Structure Design
We need an object to represent a "Pending Email":
class EmailEvent:
time: int
user_id: str
template_type: str # e.g., WELCOME, EXPIRY
original_plan: str
# For sorting
def __lt__(self, other):
return self.time < other.time
2. Process Flow
- Initialization: Iterate through users and generate initial
EmailEventsbased on the template. - Merge Changes: Iterate through
PlanChangesand convert them into "Plan Changed" events. - State Replay (Key):
- Instead of updating future events in place (which is hard), the best approach is to process all events (Emails + Changes) in chronological order.
- Maintain a
User -> CurrentPlanmap.
3. Algorithm Choice
- Method A (Sorting): Put all PlanChange events and Email events into a single list. Sort by
time. Iterate through the list: update state on Change events, and print logs using the current state on Email events.- Time Complexity:
O(N log N), where N is total events. - This is the recommended approach for interviews.
- Time Complexity:
Code Snippet (Python)
def generate_notifications(users, schedule, changes):
events = []
# 1. Generate Basic Email Events
for u in users:
start = u['start']
end = start + u['duration']
# Add Welcome
events.append({"time": start, "type": "EMAIL", "msg": "Welcome", "user": u})
# Add Expiry
events.append({"time": end, "type": "EMAIL", "msg": "Expired", "user": u})
# 2. Generate Change Events
for c in changes:
events.append({"time": c['time'], "type": "CHANGE", "new_plan": c['new_plan'], "user_name": c['name']})
# 3. Sort
events.sort(key=lambda x: x['time'])
# 4. Simulate Timeline
user_plans = {u['name']: u['plan'] for u in users}
for e in events:
if e['type'] == "CHANGE":
user_plans[e['user_name']] = e['new_plan']
print(f"{e['time']}: Plan Changed for {e['user_name']} to {e['new_plan']}")
else:
current_plan = user_plans[e['user']['name']]
print(f"{e['time']}: {e['msg']} for {e['user']['name']} ({current_plan})")
Curious about complex scheduling?
What if the schedule says "First Monday of every month"? What about time zones? oavoservice's interview coaching dives deep into these System Design follow-ups, ensuring you demonstrate architectural maturity.