Design doctor appointment booking (Practo)
The interviewer asks
“Design a doctor appointment platform like Practo. Patients search doctors by specialty and location, see available slots and book an in-clinic visit or a video consultation. Doctors manage their schedules, often also taking walk-in patients and phone bookings at the clinic.”
What a strong answer looks like
A strong answer treats this as a two-sided booking system where the supply side is messy: doctors work across several clinics, change schedules often, run late, and the clinic's front desk also books patients by phone and walk-in. Schedules are stored as rules (weekly recurring sessions with exceptions) and materialised into slots for the next few weeks; booking claims a slot with an atomic conditional update and an idempotency key. The clinic's front desk uses the same inventory (through the platform's clinic software or a sync integration), so there is one source of truth per doctor-clinic calendar. Many Indian clinics run token queues rather than exact times, so the model supports both time slots and session tokens with a live 'current token' status. Search is Elasticsearch with geo, specialty and next-available slot. Reminders reduce no-shows. Health data is sensitive, so access control, consent and audit are first-class. Deep dives: schedule rules to slots, booking correctness with offline channels, token queues and delays, and privacy.
Concepts to mention
1 Requirements
Functional
- Search doctors by specialty, symptom, locality, language, fee and next available time; view profile, clinics, ratings.
- See available slots per doctor per clinic for the next ~2-4 weeks; book in-clinic or video consult.
- Doctors and clinic staff define weekly schedules, leaves, and slot length or token limits per session; block or reschedule slots.
- Front desk books phone and walk-in patients in the same calendar.
- Reminders (SMS/WhatsApp) before the visit; cancel and reschedule; live status of delays ('doctor running 30 min late', current token).
- Pay online for video consults (and optionally prepay clinic fees); refunds for doctor cancellations.
- Prescriptions and visit notes attached to the appointment (for video consults).
Non-functional
- Scale: ~200k doctors, ~300k bookings/day, ~10M searches/day.
- No double booking of a slot; token numbers unique per session.
- Search p95 under 300 ms; booking p95 under 500 ms.
- Availability 99.95% for booking; clinic software must work during internet outages (local mode, sync later).
- Health data protected: encryption, role-based access, audit trail, consent (India's DPDP Act; ABDM standards if integrating health records).
- Reminders sent at the right local time; no reminder to cancelled appointments.
Out of scope (say it out loud)
- Video calling infrastructure (use a WebRTC provider).
- Medicine delivery and lab tests.
- Insurance claims.
2 Estimates
3 API
| Endpoint | What it does |
|---|---|
GET/v1/doctors/search?specialty=&lat=&lng=&date=&lang= | Ranked doctors with next available slot; Elasticsearch. |
GET/v1/calendars/{doctor_clinic_id}/slots?from=&to= | Free slots or token availability per session. |
POST/v1/appointments | Book {slot_id | session_id, patient_id, type}; Idempotency-Key; returns appointment and payment intent if prepaid. |
POST/v1/appointments/{id}/reschedule | Atomically move to another slot (claim new, release old). |
POST/v1/appointments/{id}/cancel | Cancel by patient or doctor; refund per policy. |
PUT/v1/calendars/{id}/schedule | Doctor updates weekly rules, leaves and slot length; regenerates future slots. |
GET/v1/sessions/{id}/live | Current token, delay estimate; SSE or poll. |
POST/v1/clinic/sync | Clinic software pushes offline bookings and pulls online ones (cursor-based). |
4 Data model
doctors / clinics / calendarsPostgresdoctor_idnamespecialtieslanguagesregistration_no; clinic_idlocation; calendar_id (doctor_id, clinic_id)feemode (slots|tokens)Source for search via CDC to Elasticsearch.
schedule_rulesPostgrescalendar_idweekdaystart_timeend_timeslot_minutes or max_tokensvalid_fromvalid_totimezone; exceptions (date, type: leave|extra)The doctor's intent. Small and edited often.
slotsPostgres partitioned by calendar_id (hash) and dateslot_idcalendar_idstart_atend_atstatus (OPEN|BOOKED|BLOCKED)appointment_idversionMaterialised from rules for a rolling 3-week window. Booking is a conditional update on one row.
sessions (token mode)Postgres + Redissession_idcalendar_iddatestart_timemax_tokensissued_tokenscurrent_tokendelay_minutesToken issue = atomic increment with limit; live status in Redis for frequent reads.
appointmentsPostgresappointment_idpatient_idcalendar_idslot_id or (session_id, token_no)typestatuschannel (online|desk|phone)payment_ididempotency_keyIndexed by patient and by calendar-date for the clinic's day view.
search indexElasticsearchdoctor_idclinics (geo_point)specialtieslanguagesfeeratingnext_available_atnext_available_at updated by events when slots change, so 'available today' filters work.
medical recordsS3 (encrypted) + Postgres metadatarecord_idappointment_idpatient_idtypes3_keyaccess_policyEncrypted per object with KMS; access logged.
5 Architecture
Hover a box to highlight its connections.
6 Request flows
Book a slot online
- 1Patient searches 'dermatologist near Koramangala, today'; Elasticsearch filters by geo, specialty and next_available_at ≤ today end, ranks by distance, rating and relevance.
- 2Patient opens a doctor and picks the 6:30 pm slot; the app sends POST /appointments with an Idempotency-Key.
- 3Appointment service runs: UPDATE slots SET status='BOOKED', appointment_id=? WHERE slot_id=? AND status='OPEN'; one row changed means success, else 'slot just taken' with alternatives.
- 4It inserts the appointment in the same transaction plus an outbox event; for video consults it creates a payment intent and confirms after payment.
- 5Events update the search index (next available), notify the clinic software, and schedule reminders.
Walk-in at the clinic during an internet outage
- 1Front desk software keeps a local copy of today's calendar and issues a booking locally, marked as pending sync.
- 2When the internet returns, it pushes pending changes with their local IDs; the sync service applies them as conditional updates.
- 3If an online patient booked the same slot meanwhile, the conflict is reported to the desk, which moves one patient to the next free slot or adds an overbook entry; the patient is notified.
Doctor running late
- 1Clinic staff mark 'doctor delayed 40 minutes' or advance the current token in the desk app.
- 2The appointment service updates live session status in Redis and emits an event.
- 3Patients with upcoming appointments that session receive a WhatsApp message with the new expected time; the app's live screen shows the current token.
7 Deep dives
From schedule rules to bookable slots
Doctors think in rules: 'Mon-Sat 10:00-13:00 at Clinic A, 15-minute slots; Tue/Thu 18:00-20:00 at Clinic B; on leave 20-25 Dec'. Store those rules as the source of truth and materialise slots for a rolling window (say 21 days) with a daily job and on every rule change. Materialised slots make booking simple: a slot is a row that can be claimed with one conditional update, and availability is a range query. Rule edits are the tricky part. When a doctor shortens a session that already has bookings, the generator must not delete booked slots; it marks them as conflicts and creates a task for the clinic to reschedule those patients, who are notified. Generate in the clinic's timezone (India has one, but the platform may expand), and make the generator idempotent by deriving slot IDs from (calendar_id, start_at), so re-running never creates duplicates. Leaves become BLOCKED slots, not deleted ones, keeping history clean. For doctors who take unlimited patients per session, skip slots and use the token model instead.
One calendar, many booking channels
The platform is not the only way patients book: the clinic receptionist takes phone calls and walk-ins, and some clinics use their own software. If these channels keep separate calendars, double booking is guaranteed. The design must make one inventory the source of truth per doctor-clinic calendar, which is the platform's slot table, and give every channel access to it. The clinic desk app (the platform's own practice software, as Practo does with its clinic product) books through the same API. For clinics using third-party software, a sync integration pushes and pulls bookings with cursors and idempotency keys. Clinics lose internet often, so the desk software works offline from a local copy and syncs later; conflicts are then possible and must be resolved explicitly, not silently overwritten: online bookings win by default and the desk sees the conflict. To reduce conflicts, doctors can reserve a share of slots for walk-ins and phone bookings, exposing only part of the session online.
Token queues and live delays
Many Indian clinics do not run exact appointment times: they run sessions where patients get token numbers and the doctor sees them in order. Supporting only fixed slots misses how most of the market works. In token mode, a session has max_tokens and issued_tokens; booking issues the next token with an atomic increment bounded by the limit (a conditional UPDATE issued = issued + 1 WHERE issued < max, returning the new value). The desk app advances current_token as patients go in. Live status is read far more often than it is written (patients refreshing 'how many before me'), so keep it in Redis and push changes via SSE or poll every 30-60 s. Estimate waiting time from the doctor's moving average time per patient in this session and recent days. Notify patients when they are ~3 tokens away, which lets them wait at home instead of a crowded waiting room. Late arrivals and emergencies break strict order; let the desk re-order, and show patients an estimate, not a promise.
Health data privacy and access control
Appointments reveal sensitive facts: a booking with a psychiatrist or an oncologist is health data even without notes. Treat the whole system accordingly. Encrypt data at rest and in transit, encrypt documents per object with KMS keys, and restrict staff access through roles: a clinic receptionist sees today's appointments for their clinic, not patients' histories; doctors see records only for patients who booked with them or consented to share. Log every read of medical records in an append-only audit log and review unusual access. Keep minimum data in notifications: 'Your appointment at 6:30 pm tomorrow' without the specialty. Under India's DPDP Act you need clear consent for each purpose, deletion on request (subject to medical record retention rules), and breach notification. If you integrate with the national health stack (ABDM, with ABHA health IDs), records are shared only through consent artefacts. Analytics and ranking must use de-identified data.
8 Trade-offs
Materialised slots vs computing availability on the fly
Computing free time from rules minus bookings at read time saves storage but makes booking harder (you must lock a time range, not a row) and search slower. Materialised slots cost ~150M small rows and a generator job, but give simple atomic booking and fast range reads. Choose materialised slots; use time-range constraints only where slot lengths vary.
Postgres for slots vs a NoSQL store
Booking needs conditional updates, transactions with the appointment row, and range queries by date. Postgres partitioned by calendar handles ~30 bookings/s trivially. A NoSQL store adds nothing here; the scale is moderate.
Allowing overbooking
Some doctors overbook on purpose because of no-shows (often 10-20%). Support it explicitly with a per-session overbook limit, rather than by bypassing constraints, so the system still knows what is promised.
9 Bottlenecks
Search freshness for 'available today'
If next_available_at is stale, patients click doctors with no free slots. Update it from booking events within seconds, and verify slots live when the profile opens. At 10x, batch index updates per doctor every few seconds.
Morning booking rush for popular doctors
Popular doctors' slots open at a fixed time and fill in minutes. Contention is per slot, so database row locks handle it; rate limit per patient and stop showing a slot as available as soon as it is claimed to reduce failed attempts.
10 Failure handling
Reminder service down
Reminders are scheduled jobs in a durable store; when the service recovers it sends overdue reminders only if the appointment is still in the future and not cancelled. Missing a reminder increases no-shows but breaks nothing.
Clinic offline for hours
Online booking continues against the platform calendar; the desk works locally. On reconnect, sync resolves conflicts explicitly. Optionally, stop online bookings for today's remaining slots when a clinic is offline for long, to reduce conflicts.
Payment succeeds but booking fails
For prepaid video consults, payment confirmation and slot claim are tied by the appointment's idempotency key. If the slot was lost, refund automatically or offer the next slot with the same doctor.
11 What impresses
- Brings up walk-ins, phone bookings and token queues, which is how many Indian clinics actually run.
- Separates schedule rules from materialised slots and handles rule edits that hit booked slots.
- Designs clinic software for offline operation with explicit conflict handling.
- Keeps next-available time in the search index fresh via events.
- Treats even appointment metadata as sensitive health data.
- Supports deliberate overbooking with limits instead of bypassing constraints.
12 Mistakes
- Assuming the platform is the only booking channel.
- Computing availability from rules on every search request.
- Deleting booked slots when a doctor changes the schedule.
- No reminders or delay notifications, leading to no-shows and crowded waiting rooms.
- Putting the specialty or diagnosis in SMS reminders.
- Over-engineering with distributed locks for a low-QPS booking path.
13 Follow-ups
Lessons this design uses
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.