khel-arena
Futsal Booking Platform
About 5 staff hours saved a day, 15% more revenue
- Go
- SvelteKit
- PostgreSQL
What it is
A website where people in Kathmandu can see which futsal courts are free and book one in about thirty seconds, instead of calling the arena and hoping the slot is still open when they arrive.
Who it helped
Futsal is how a lot of people in Kathmandu see their friends during the week, and booking was the annoying part. You called the arena, someone flipped through a paper notebook, and half the time you were told to call back later. Groups regularly turned up to find their slot given to someone else, because two phone calls came in at the same time and only one of them got written down.
The site turns that into something a player can do on their phone while the group chat is still arguing about the day. You pick a date, see every free hour and what it costs, pay with eSewa or Khalti, and the slot is yours before you close the tab. Nobody has to trust that a note was written down correctly.
For the arena staff the change was bigger. They were losing around five hours a day to answering the same three questions on the phone, and that time went back into actually running the place. Evening slots that used to sit empty because nobody picked up the phone now get filled, and revenue is up about fifteen percent. The part I am most glad about is that double bookings are gone. Not rarer, gone: the database itself refuses to store two live bookings on the same court at the same time, so no amount of Friday night traffic can sell the same hour twice.
What went well
Two decisions did most of the work. The first was letting the database enforce the one rule that must never break. A single Postgres constraint makes two overlapping bookings on the same court impossible to save, so the guarantee does not depend on me writing careful code every single time. There is a test where twenty people try to grab the same hour at once and exactly one of them wins.
The second was not storing the calendar at all. Instead of creating a row for every hour of every court forever, the grid of free slots is worked out on request from the arena's opening hours, its price rules and the bookings that already exist. There is nothing to keep in sync, so a slot cannot look free on the page while being taken in the database.
What's next
Owners still cannot change their own opening hours or prices without me. That is the next thing to build, because a platform that needs the developer for a Tuesday holiday closure is not really theirs yet.
Cancellations also need to be clearer to players. An earlier version had the page treating a no-show slot as free while the database still counted it as taken, so someone would click an open slot and get an error for no reason they could see. Both sides now come from one definition of what "taken" means, and that is something I would set up on day one next time.