DEV Community

Revenue Search
Revenue Search

Posted on

What running a cleaning company taught me about scheduling systems

I don't write software for a living. I run a cleaning company in Toronto. But the last two years of trying to schedule crews across a city has taught me more about constraint solving than any tutorial ever did, and some of it might be useful if you're building anything with appointments in it.

The naive model breaks immediately

My first system was a spreadsheet: job, address, time, cleaner. It worked for about eleven jobs. The moment you have two crews and a city, you're not scheduling appointments anymore, you're solving a routing problem with soft constraints:

  • Travel time between jobs is not symmetric (rush hour on the DVP going north is not the same as going south)
  • Job duration is a guess, and a wrong guess cascades into every job after it
  • Some jobs are hard-pinned (a move-out clean has to be done before the landlord walkthrough at 4pm)
  • Some cleaners can't take certain jobs (allergies, no ladder, no car)

The classic mistake is treating duration as fixed. It isn't. A 3-bedroom deep clean is 3 to 5 hours depending on how much grease is on the stove, and you don't know until you're standing in the kitchen.

What actually helped: buffers as a first-class concept

The fix wasn't a smarter algorithm, it was admitting uncertainty in the data model. Every job now carries a duration_min and duration_max, and the scheduler packs against the max. We lose some theoretical throughput. We stopped being late, which is worth vastly more.

If you're modelling anything real-world with time in it, store the range, not the point estimate. Averages lie in exactly the case you care about.

Idempotency matters when humans are the API

Crews text updates. Clients reply to confirmations. Someone reschedules twice. Early on, a double-confirm would create two jobs and two invoices, which is a genuinely embarrassing way to learn about idempotency keys.

Now every state change is keyed on (job_id, transition), and replaying it is a no-op. Same principle as any webhook handler, just with more human chaos on the input side.

Publish the constraints to your users

The last thing, and it's more product than engineering: we put our actual prices and time windows on the site instead of hiding them behind a quote form. It cut the back-and-forth enormously because people self-select. Our flat-rate cleaning services in Toronto page is basically the constraint set made public, and it does more scheduling work than any feature I've built.

If your booking flow requires a phone call to discover the price, your scheduler isn't the bottleneck. Your information architecture is.

Top comments (0)