You are building an email notification system for a subscription service. The system needs to automatically send emails to users at specific points in their subscription lifecycle, such as when they first subscribe, before their subscription expires, and when it actually expires.
The problem is divided into three progressive parts:
Basic Email Scheduling: Generate scheduled emails based on a configurable notification timeline
Plan Changes: Handle mid-subscription plan changes and update subsequent notifications
Subscription Renewals: Support subscription extensions and adjust notification schedules accordingly
Generate email notifications in chronological order by day
Support configurable notification schedules with dynamic timing rules
Handle multiple users with overlapping subscription periods
Maintain accurate subscription state as changes occur
The key challenge is designing a reusable, extensible system that can accommodate various notification schedules without hardcoding specific timing rules.
There are multiple parts to this problem -- ask the interviewer how many parts there are to better manage your time
The send_schedule should be handled generically -- do not hardcode logic for specific offsets like -15
Write your own test cases and ensure your code compiles and runs correctly
Implement a function send_emails(user_accounts, send_schedule) that generates scheduled email notifications for subscription lifecycle events.
The send_schedule parameter defines when to send emails:
"start": Reserved key for the subscription start date
"end": Reserved key for the subscription end date
Numeric keys: Represent days relative to the end date (always negative, e.g., -15 means 15 days before expiration)
Your solution should be general and reusable -- it should work with any numeric offset values in the schedule, not just -15. The code should dynamically identify which keys are numeric offsets versus the reserved "start" and "end" keywords.
[ {"name": "John", "plan": "Silver", "account_date": 0, "duration": 30}, {"name": "Alice", "plan": "Gold", "account_date": 1, "duration": 15}, ]
{ "start": "Welcome", -15: "Upcoming expiration", # 15 days before end "end": "Expired", }
Group emails by day number in ascending order
Each line format: {day}: [{message_type}] Subscription for {name} ({plan})
Multiple emails on the same day should appear on separate lines
Days with no emails should be omitted from output
Return the result as a single string (not printed)
Dynamic Schedule Handling: Don't hardcode logic for -15 specifically. Your code should identify numeric keys programmatically and calculate their dates based on the subscription end date.
Subscription Period: A subscription starting on day X with duration D is active on days [X, X+D-1] and expires on day X+D.
Relative Date Calculation: For a numeric offset like -15, calculate the notification date as end_date + offset (since offsets are negative, this subtracts days).
Should we assume the send_schedule keys are always either "start", "end", or negative integers?
What should happen if a relative notification date (e.g., -15) falls before the subscription start date?
Are there constraints on the range of numeric offsets?
Should emails for the same user on the same day be in a specific order?
Extend your solution to handle mid-subscription plan changes. Users can switch to a different plan during their active subscription period, which should:
Generate a "Changed" notification on the change date
Update the plan name in all subsequent notifications for that user
Keep the original subscription end date unchanged
[ {"name": "John", "new_plan": "Gold", "change_date": 5} ]
user_accounts = [ {"name": "John", "plan": "Silver", "account_date": 0, "duration": 30}, {"name": "Alice", "plan": "Gold", "account_date": 1, "duration": 15}, ] changes = [{"name": "John", "new_plan": "Gold", "change_date": 5}] send_schedule = {"start": "Welcome", -15: "Upcoming expiration", "end": "Expired"}
0: [Welcome] Subscription for John (Silver) 1: [Welcome] Subscription for Alice (Gold) 1: [Upcoming expiration] Subscription for Alice (Gold) 5: [Changed] Subscription for John (Gold) 15: [Upcoming expiration] Subscription for John (Gold) 16: [Expired] Subscription for Alice (Gold) 30: [Expired] Subscription for John (Gold)
On day 5, John's plan changes from "Silver" to "Gold"
The "Changed" notification shows the new plan name
John's day 15 and day 30 notifications now display "Gold" instead of "Silver"
Alice's notifications remain unchanged
Plan changes only affect the plan name, not the subscription duration or end date
All notifications after the change date should reflect the new plan
A user can have multiple plan changes during their subscription (process them chronologically)
Change date must be within the active subscription period (at or after start, before end)
Add support for subscription renewals (extensions). When a user renews during their active subscription:
Generate a "Renewed" notification on the renewal date
Extend the subscription end date by the renewal duration
Recalculate all future notification dates based on the new end date
Important: Renewals can only occur during the active subscription period. The extension begins on the original expiration date, not on the renewal date.
The changes array can now include both plan changes and renewals:
`
{"name": "John", "new_plan": "Gold", "change_date": 5}
{"name": "Alice", "extension": 15, "change_date": 3} `
0: [Welcome] Subscription for John (Silver) 1: [Welcome] Subscription for Alice (Gold) 1: [Upcoming expiration] Subscription for Alice (Gold) 3: [Renewed] Subscription for Alice (Gold) 5: [Changed] Subscription for John (Gold) 15: [Upcoming expiration] Subscription for John (Gold) 16: [Upcoming expiration] Subscription for Alice (Gold) 30: [Expired] Subscription for John (Gold) 31: [Expired] Subscription for Alice (Gold)
Original end date: 1 + 15 = 16
Scheduled notifications: Day 1 "Welcome", Day 1 "Upcoming expiration" (16-15=1), Day 16 "Expired"
Extension: 15 days
New end date: 16 + 15 = 31
Send "Renewed" notification
Remove future notifications scheduled before renewal (the day 16 "Expired")
Regenerate notifications based on new end date:
Day 1: "Welcome" (already sent, skip)
Day 16: "Upcoming expiration" (31-15=16) -- new notification
Day 31: "Expired" -- new notification
Key insight: Alice receives TWO "Upcoming expiration" notifications:
The subscription period is [account_date, account_date + duration) (exclusive end). When renewed, the extension is added starting from the original expiration date: new_end = original_end + extension.
Distinguish between plan changes and renewals by checking for new_plan vs extension fields
Renewals must occur during the active subscription period
The renewal notification uses the current plan name (after any prior plan changes)
Remove or update future notifications that were scheduled before the renewal
Recalculate relative notifications (like "Upcoming expiration") based on the new end date
Renewal after a plan change (use the updated plan name)
Multiple renewals for the same user
Renewal on the same day as a plan change (process renewals after plan changes)
Very short subscriptions where renewal pushes notifications backward