← /writing/case-study·2026 · 01 · 31·5 min read

Building a Multi-Location Turf Booking Platform Across India

Booking systems look easy from the outside. The hard part is what happens between the booking confirmation and the player arriving.

companion: Pessimistic vs optimistic concurrency for slot reservation: a benchmark and a decision

Booking systems look easy from the outside. The booking UI takes ten minutes to build. The hard part - the part most platforms get wrong - is what happens between the booking confirmation and the player arriving.

This client operated a network of sports turfs across multiple Indian cities. The product was a booking engine. The reality was an operations platform.

Context

A turf is real-world inventory: a physical court with a finite number of bookable hours per day, peak-time pricing, weather sensitivities, and a manager on site. Multiply that by multiple cities, multiple sports, multiple turf-types per city, and you have a multi-tenant inventory problem with very specific edge cases.

The platform had to coordinate three populations:

  • Players booking slots from a phone, mostly mobile-first, often on the way to a game.
  • Turf operators running the day-to-day at each location - equipment, staff, weather calls.
  • The central operator owning the network - pricing, marketing, expansion, finance.

A booking-only product would have been a thin commerce app. The actual product had to absorb the operational reality underneath each booking.

The product challenge

Race conditions everywhere.

A turf has 14 bookable slots a day. On a Friday evening, three players try to book the 7-9pm slot within seconds of each other. One has to win, two have to be told politely. The wrong design here ships a double-booking, which destroys trust faster than any other failure.

Compound that with peak-hour pricing (the same slot costs more on Friday evening than Tuesday afternoon), cancellation rules (refund window, no-show forfeits), buffer times (the cleaning crew needs 15 minutes between bookings), and operator overrides (this turf is closed for resurfacing on Saturday) - and the booking engine has to be a real concurrency-aware piece of software, not a CRUD app.

My role

I led product and engineering: the booking engine, the inventory model, the multi-city operator hierarchy, payment integration, the player and operator apps, and the central operations console.

Core features

  • Multi-city, multi-location inventory: locations, turfs, slot definitions, peak/off-peak rules.
  • Booking engine: concurrency-safe slot reservation, atomic payment-and-confirmation, cancellation flow.
  • Pricing rules: base price, peak hours, weekend uplift, member discounts, promo codes.
  • Cancellation and refund rules: time-window-based refund logic, no-show handling, operator-discretion overrides.
  • Buffer-time management: automatic gaps between bookings for cleaning / equipment changeover.
  • Operator app: daily schedule, on-the-fly closures (weather), customer arrivals, payment confirmations.
  • Central console: network-wide utilization, location performance, financial reconciliation across cities.

Technical highlights

Slot reservation uses pessimistic locking on the inventory row at the moment of booking. The first request to reach the lock wins; the rest see "already booked" within the same second. We benchmarked this against optimistic concurrency control and the reality of the use case - three players hitting the same slot - proved that pessimistic was the right tradeoff. Conflicts here are not rare; they are the load case.

Pricing computes at booking time, not at slot-definition time. A slot doesn't have "a price"; it has a price function evaluated against the booking context (player tier, day of week, time of day, active promotions). This decoupling let the central operator change pricing across the network without re-writing inventory rows.

Cancellation rules are themselves a state machine with parameterized windows. A booking cancelled 24 hours before is full refund; 6 hours before is 50%; under 1 hour is no refund. The state machine handles edge cases - operator-initiated cancellation (weather), player-initiated cancellation, and no-show - with distinct flows for each.

The multi-city hierarchy was modeled as a tree (network → city → location → turf → slot) with permissioned views at each level. A turf operator at one location only saw their own turf. A city manager saw the locations in their city. The central operator saw everything. Pricing rules cascaded down the hierarchy with overrides at any level.

What this taught me

The complexity behind calendar-based booking systems. A "calendar" is a UI element. A booking inventory model is a concurrency-and-pricing-and-rules engine. Treating one as the other ships double-bookings and lost revenue. Build the inventory model first; build the calendar UI on top of it.

Buffer time is not garnish. Operationally, the 15 minutes between bookings is when the cleaning happens. Skip it and the next player walks onto a wet floor. Build it as a hard constraint on the engine, not a hint in the UI.

Multi-city operations need centralization that respects local autonomy. A turf operator who can't make a real-time decision (close due to weather, refund a regular customer) without a head-office call won't trust the platform. A network without central visibility can't make pricing or expansion decisions. The right design gave both - operator autonomy at the slot level, central visibility at the strategic level.

Outcome

The platform consolidated multi-city booking, payment, and operations into a single coherent system. Players got reliable booking with predictable pricing across cities. Turf operators got a daily-schedule app that surfaced what they needed to act on without buried menus. The central team got network-wide visibility that turned expansion decisions from anecdote into data. Double-bookings became near-zero - the most-cited improvement from the operator side.


If you are building a booking platform for real-world inventory: do not start with the calendar. Start with the inventory model - concurrency, pricing, rules, buffers - and treat the calendar as the thinnest possible UI on top of it. The discipline is invisible to users until something breaks. Then it is the only thing they care about.