DEV Community

Mriganka Bhuyan
Mriganka Bhuyan

Posted on

I built a pet grooming software. The calendar was the easy part

I assumed booking would be one of the easier parts of building a SaaS product.

Show a calendar. Find an empty slot. Insert a row into a database.

Then I started building June.

June is pet grooming software for independent shops. It handles online booking, reminders, client records, checkout, waivers, vaccines, and the everyday work around an appointment.

The more I learned about grooming shops, the more obvious it became that a generic scheduling component was not going to work.

The calendar UI was the easy part.

The hard part was deciding whether a slot was actually real.

The problem was not a lack of features

While researching established grooming platforms, I kept seeing the same complaints.

Too many screens. Too many fields. Too many settings. Multiple ways to do the same thing.

That does not necessarily mean those products are badly engineered. Grooming has a surprising amount of domain complexity, and mature products accumulate years of edge cases.

The problem is where that complexity ends up.

In a lot of software, it gets handed directly to the user. The shop owner has to understand the configuration, remember the exceptions, and notice when two rules disagree.

I wanted June to take the opposite approach.

The groomer should see a simple list of times that can actually be booked. The engine should do the complicated work required to produce that list.

A grooming appointment is not just an empty rectangle

To decide whether 10:00 AM is available, June may need to consider:

  • The duration of the selected service
  • Extra time added by selected add ons
  • The groomer's recurring working hours
  • Approved schedule exceptions for that day
  • The shop's operating hours for that service
  • Existing confirmed appointments
  • Slots temporarily held by other customers
  • Minimum booking notice and same day rules
  • The shop's timezone

None of that is especially exotic by itself.

The interesting problem appears when two people try to book the same groomer at the same time.

Imagine two customers open the booking page within a few seconds of each other.

Both see the final 2:00 PM appointment. Both click it. Both start entering their pet's information.

An availability check in the UI cannot guarantee that both customers will not confirm it. By the time the booking request reaches the server, the answer may have changed.

June handles this with temporary slot holds.

When a customer selects a time, the server tries to place a ten minute hold on that groomer and time range. That gives the customer enough time to finish without letting someone else take the appointment halfway through the flow.

Creating the hold is an atomic database operation. It checks confirmed bookings, checks active holds, and serializes competing operations for the same groomer.

Confirmation is also server authoritative.

The browser does not get to decide the final duration, price, customer, pet, or availability. The server reloads the relevant records, recomputes the booking facts, locks the hold, checks the time again, creates the booking, and consumes the hold inside the transaction.

Application checks are useful, but they are not enough for concurrency sensitive rules.

Simple software still has complexity

Building this engine took much longer than dropping a calendar library onto a page.

It required more database work, more failure cases, and tests for situations that are difficult to reproduce by clicking around locally.

But this is the part of vertical SaaS I find interesting.

Complexity does not disappear. You choose who has to carry it.

The product can make the customer manage it through more fields, settings, and instructions.

Or the engineering can absorb it so the customer sees a handful of appointment times that simply work.

With June, I am betting on the second approach.

If you have built software for a specific industry, what workflow looked simple until you learned the actual domain rules?

Top comments (0)