What I Learned Building a Booking Platform from Scratch
Lessons from building a multi-tenant booking system for barbershops — from real-time availability to payment processing.
What I Learned Building a Booking Platform from Scratch
Lessons from building a multi-tenant booking system for barbershops — from real-time availability to payment processing.
What I Learned Building a Booking Platform from Scratch
Most booking tools weren’t built for barbershops. They were built for generic “service businesses” and stretched to fit. I wanted to build something that actually understood how a shop runs — walk-ins, multiple barbers, tight schedules, and customers who just want to pick a time and pay.
The real problem
The shops I talked to had the same complaints: existing platforms took a cut of every booking, locked them into a marketplace they didn’t want to be in, and made it hard to brand the experience as their own.
So the goal was simple — give each shop their own branded booking page, let them keep their payments, and make the whole thing work in under five minutes.
Multi-tenancy was the hard part
Each shop needed to feel like its own app. That meant subdomain-based routing, per-tenant theming, and isolated data — but all running on a single codebase and database.
The tenant resolution layer runs early in the request lifecycle. A middleware checks the subdomain, looks up the shop, and injects the tenant context into every downstream query. I cached this lookup aggressively because it runs on every single request.
// Simplified tenant resolution
const subdomain = getSubdomain(request.headers.get('host'));
const cacheKey = 'shop:' + subdomain;
const shop = await cache.getOrSet(
cacheKey,
() => db.shops.findBySubdomain(subdomain),
{ ttl: 300 }
); Real-time availability
Booking slots needed to account for existing appointments, barber schedules, break times, and service durations — all in real time. I landed on 15-minute intervals as the base unit. Every time a customer views available times, the system computes open slots by subtracting booked time from each barber’s schedule.
The tricky part was handling concurrent bookings. Two customers viewing the same slot at the same time. I solved this with a short-lived reservation — when you select a time, it’s held for a few minutes while you complete payment.
Payments without the platform tax
I used Stripe Connect so each shop has their own Stripe account. Customers pay the shop directly — no middleman fees from the platform. This was a major selling point, but it added complexity. Onboarding a new shop meant guiding them through Stripe’s identity verification flow without losing them in the process.
What I’d do differently
If I started over, I’d invest in end-to-end tests earlier. The interaction between booking, payments, and notifications has enough edge cases that manual testing doesn’t scale. I’d also build the admin dashboard mobile-first — most shop owners check their bookings from their phone, not a desktop.
The result
The platform is live, processing real bookings, and the feedback from shop owners has been solid. The thing I’m most proud of is the onboarding — a new shop can go from signup to accepting their first booking in about five minutes.
Building something similar? Let’s talk.