Lessons from designing booking systems

· 2 min read · #backend #product #scheduling

Booking looks like a solved problem from the outside: show free slots, let someone take one. Then you meet the second user pressing confirm at the same millisecond, a staff member changing their working hours retroactively, and a client in another time zone reading the wrong number on a screen.

Availability is derived, never stored

The mistake I see most often is a table of free slots. It goes stale immediately. Availability should be computed from three things — working hours, existing bookings, and blocked time — at the moment someone asks.

-- The authoritative check happens at write time, not read time.
insert into bookings (staff_id, starts_at, ends_at, client_id)
select $1, $2, $3, $4
where not exists (
  select 1 from bookings b
  where b.staff_id = $1
    and b.status = 'confirmed'
    and tstzrange(b.starts_at, b.ends_at) && tstzrange($2, $3)
);

An exclusion constraint on the range does the same job at the database level, which is where I prefer it: the rule survives every future code path.

Store instants, render locally

Everything in the database is UTC. Formatting happens at the edge, in the viewer's zone, with the business's zone shown explicitly when the two differ. A booking confirmation that says "15:30" without saying whose 15:30 is a support ticket waiting to happen.

Cancellation is a first-class flow

Half the product is what happens after the booking: reschedules, late cancellations, no-shows, refunds, reminders that must not fire for cancelled appointments. Designing that up front is much cheaper than bolting it on once real money is attached to each slot.

Small details people notice

  • Show the price and duration before the calendar, not after.
  • Keep the number of taps to book under five.
  • Send the reminder at a time a human would send it.
  • Never lose a form because the session expired.

Scheduling rewards the same thing payments do: care about the edges. Most of the work nobody sees is what makes the visible part feel effortless.

← All writing