Smart Rota Generation

One guard. One shift. Five different systems all trying to hand it to him at once. That was the bug nobody was looking for.

Our scheduling product at AgileForce fills shift gaps automatically – a guard calls in sick, a manager approves a swap, someone doesn’t answer the pre-shift call. The system finds a replacement fast, usually without the manager lifting a finger.

Over about six months, we built five separate flows to cover five separate reasons a shift goes empty. Each one worked perfectly on its own. Each one sent a push notification saying “this shift’s available, tap to claim it.”

The bug wasn’t in any single flow. It was in the gap between them.

Picture a guard on a Friday afternoon. The leave-coverage flow offers him Friday night because the assigned guard just booked leave. The pre-shift flow offers him the exact same shift because a second guard declined. The swap flow offers it too. Three systems, one slot, three notifications sitting on his phone.

He taps one and claims it. Great. The other two are still sitting there, still tappable, with zero idea anything happened. He taps a second one, and now the system thinks he’s double-booked, the site’s overstaffed, payroll’s wrong, and two other guards got told “sorry, taken” for a shift they never actually lost to anyone real.

The obvious fix – “cancel the other offers when someone accepts” – wasn’t obvious at all, because each flow stored offers differently, named cancellations differently, and connected to the shift through a completely different chain of database tables. There was no single place to even ask “what else is this person currently holding?”

So instead of teaching all five flows about each other, we gave that question one owner. We wrote one function that knows where every type of offer lives and how it connects back to the shift.

When a guard accepts an offer, it finds every other pending offer for that same guard and shift, cancels them, and triggers the appropriate notifications.

Every accept handler calls it. Adding a sixth flow later means touching one function, not auditing five.

The real lesson had nothing to do with scheduling. The moment more than one feature can claim the same thing, someone needs to own that question – or every feature answers it differently, and the collision happens exactly where nobody thinks to look.

Follow for more of these.

Share this :

Leave a Reply

Your email address will not be published. Required fields are marked *

Latest blog & articles

MING

A progress bar that just... froze. That's how this whole thing started. Another...

Sway

I believe the most dangerous performance bug is the one that looks fixed. ...

Get Free Consultation!