BlogMarch 20, 2026

How I structure a booking system from scratch

The decisions that matter before writing the first line of code
Sim Kritamook
A booking system at its core is answering one question: can this resource be reserved for this time, and what happens if it can? That sounds simple. In practice, it involves availability logic, conflict detection, payment state, notifications, admin visibility, and a long list of edge cases that only surface once real users are interacting with the system. Starting with the data model before writing any UI saves a significant amount of rework. The first question is: what is being booked? A room, a court, a time slot with a specific instructor, a seat on a tour? The answer shapes everything else. Resources have availability windows, capacity, and often nested relationships. A hotel might have room types with multiple units. A sports facility might have courts that share certain equipment. A tour operator might have guides with different certification levels tied to different trips. Mapping this out explicitly before building prevents the "we forgot about X" conversations that derail projects mid-build. The most technically sensitive part of any booking system is ensuring two bookings cannot claim the same resource at the same time. This sounds obvious but gets complicated quickly. Key considerations:
  • Buffer time: Many bookings need gap periods between them (cleaning, setup, travel)
  • Partial availability: Resources available only on certain days, hours, or seasons
  • Hold states: Reservations in progress that have not completed payment yet
  • Cancellation and rebooking: Making a slot available again cleanly without creating orphan records
The safest approach is treating availability as a function of the booking state machine rather than a static flag on the resource. A booking is rarely just "confirmed" or "cancelled." A realistic state machine looks more like: pending → awaiting_payment → confirmed → completed with parallel paths for cancelled, refunded, and no_show. Each transition has rules: what triggers it, who can initiate it, and what downstream actions it causes (notification, calendar update, inventory adjustment). Designing this explicitly means the system behaves consistently even in unusual sequences of events. Customer-facing interfaces get most of the design attention, but the admin layer is what keeps a booking business running day to day — see the admin dashboard features most booking platforms skip for what that layer should actually include. Useful admin tooling includes:
  • A calendar view that shows all bookings at a glance
  • The ability to manually create, edit, or override bookings
  • Reporting on utilisation, revenue, and cancellation rates
  • Notification logs so staff know what has been sent
A booking system without a strong admin layer forces staff to work around the software, which defeats the purpose. Automated notifications reduce manual work but introduce their own complexity. The main questions:
  • What events trigger notifications? (booking created, reminder, cancellation)
  • Who receives them? (customer, staff, third parties)
  • What channel? (email, SMS, LINE — relevant for Thailand)
  • What happens if delivery fails?
Building the notification system as a separate layer from the booking logic makes it easier to adjust without touching the core data model. The biggest source of rework is not technical — it is underspecified requirements. "The customer should receive a confirmation email" contains at least five open questions before I write a line of code. The projects that go smoothly are the ones where we take the time to map the full flow before building any of it.
Share this post: