Calendar and appointment scheduling systems
Designing calendars and booking of time slots: events and recurrence rules, expanding recurring events, time zones, free/busy queries, finding meeting times, invitations and RSVPs, appointment slots for providers, preventing double booking, reminders and syncing with external calendars.
Reading is half of it. See this used in a real interview: walk through Design a Project-to-Contractor Matching System →
Calendars (Google Calendar, Outlook) and appointment booking (doctors, salons, contractors, interview scheduling) look simple and hide some of the trickiest data modelling in everyday software: recurring events with exceptions, time zones and daylight saving, availability across many people, and double-booking prevention. They appear as interview questions in their own right and inside marketplace designs.
Events and recurrence
An event has a start, end (or duration), time zone, title, attendees and optional recurrence. Recurring events use a recurrence rule (the iCalendar RRULE standard): "every weekday at 9:00", "every second Tuesday", "monthly on the last Friday until December".
Store the rule, not every occurrence:
- The series: rule, start, time zone, duration.
- Exceptions: occurrences moved or cancelled (keyed by the original occurrence time).
- Overrides: modified single occurrences ("this one is at 10:00 instead").
When displaying a week, expand the rule for that window, apply exceptions and overrides. Expansion must happen in the event's time zone so "9:00 every Monday" stays at 9:00 local time across daylight saving changes. See time zones and dates.
For fast range queries, many systems also materialise occurrences for a rolling window (for example the next year) into an index, regenerated when the series changes. Unbounded series are never fully expanded.
Editing recurring events
"Change this event", "this and following" and "all events" are different operations:
- This: an override for one occurrence.
- This and following: split the series at that occurrence into two series.
- All: modify the series and re-apply exceptions where still valid.
These are classic sources of bugs; model them explicitly.
Free/busy and finding a time
- Free/busy queries ask: in this window, when is each person busy? Answer from materialised occurrences, returning busy intervals without details (privacy).
- Finding a meeting time intersects the free intervals of all attendees within working hours (in each person's own time zone), then ranks candidates.
- For many attendees or rooms, precompute busy intervals per person per day and cache them.
Invitations and RSVPs
The organiser's event is the source of truth; each attendee has an attendance record (needs action, accepted, declined, tentative). Changes by the organiser propagate to attendees' calendars; RSVPs update the organiser's view. Across systems, invitations travel as iCalendar messages by email. Use events or a queue for propagation. See event-driven architecture.
Appointment booking
Provider scheduling (a contractor, a clinic, an interviewer) is a calendar plus a booking problem:
- Availability comes from working hours rules minus existing events minus buffers (travel time, breaks), computed for a window and offered as slots.
- Booking a slot must be atomic: claim the slot with a unique constraint on (provider, start time) or a conditional insert checking for overlap, so two customers cannot book the same time. See bookings and reservations and optimistic vs pessimistic locking.
- Holds: reserve a slot briefly during checkout or payment, releasing on expiry.
- Display availability from cache, re-check at booking time.
See Design Contractor Matching.
Reminders
"10 minutes before" reminders are delayed jobs computed from each occurrence's start time, in the right time zone, and recomputed when events change. For millions of users, bucket timers by minute and add slight jitter. See delayed jobs and distributed cron and Design a Notification System.
Syncing external calendars
Users connect Google or Microsoft calendars:
- Use provider APIs with incremental sync tokens and push notifications (webhooks) for changes. See webhooks.
- Map external events into your busy times; write your bookings back to their calendars.
- Expect delays and conflicts; re-check availability against the latest data at booking time, and handle the rare double booking with a clear resolution process.
- Store OAuth tokens securely and refresh them. See OAuth 2.0 and OpenID Connect.
Storage and scale
Partition events by calendar owner; index occurrences by (calendar, start time). Read patterns are week and day views for one person, and free/busy for several, so per-user partitioning works well. Shared calendars (rooms, teams) are separate calendars with their own permissions. See authorization and permissions.
In the interview
"Events store an RRULE, time zone, exceptions and overrides; occurrences are expanded for views and materialised for the next year into an index by calendar and start time for free/busy. Booking a provider slot is a conditional insert guarded by a uniqueness or overlap constraint, with short holds during payment. Reminders are bucketed delayed jobs; external calendars sync incrementally via webhooks and are re-checked at booking."
Checklist
- Recurrence rules with exceptions and overrides; expansion in the event's zone.
- Materialised occurrences for a bounded window.
- Explicit "this", "this and following" and "all" edits.
- Free/busy intervals and meeting-time intersection across zones.
- Atomic slot booking with constraints and expiring holds.
- Reminders as delayed jobs; incremental external calendar sync.