How can I achieve a better temporal spread of shifts across all employees, and include balanced holiday coverage as well?

I’m using Google OR-Tools to build an on-call scheduler, and it works welshifts are evenly distributed, and constraints like avoiding back-to-back assignments are in place.

However, I’m noticing that the scheduler tends to “burn through” one person’s shifts early before moving on to others. I’d like to Shift Up And Spread assignments more evenly over time.

Additionally, is there a way to layer in holiday-specific periods and ensure those are also fairly distributed, almost like a sub-shift category?

How can I achieve a better temporal spread of shifts across all employees, and include balanced holiday coverage as well?

I ran into the same thing with OR-Tools where the model would front-load one person’s shifts. What worked for me was introducing a time-weighted penalty in the objective function.

Basically, I penalized assignments that occurred too close to a previous assignment for the same person, not just consecutive ones, but within a 3-5 slot window.

This helped Shift Up And Spread assignments more evenly across the timeline without breaking fairness. Try tweaking the objective weights to discourage early clusterings.

This sounds familiar! In my case, I used sequence constraints, specifically soft negative constraints on shift distance per employee.

I defined a custom interval variable for each person and then added penalties if two shifts were too close in time. That way, the solver naturally preferred solutions that Shift Up And Spread assignments over time.

For holiday coverage, I flagged holiday dates and added a per-employee counter to balance those too. Worked great for rotating long weekends!

When I had to distribute holidays fairly, I tagged each shift with a holiday: true/false property and tracked how many holidays each person received. Then I constrained that count to be within ±1 of each other.

Combined with a rolling window check (e.g., no more than 2 shifts every 6 slots), this approach helped Shift Up And Spread the load naturally.

You may want to use a weighted max constraint to allow some leeway but still bias toward fairness.