ds@mcpanalytics.aiEmail your data and your question and get an answer about your own data, with the method shown.Email your data and question. Get an answer.
WHITEPAPER

Workforce Scheduling Optimization: A Method Guide

By MCP Analytics Team | Published , updated

By MCP Analytics Team • • 9 min read • Operational Analytics

Executive Summary

This paper is an explanation of a method, not a study. It describes how workforce scheduling is framed as an optimization problem: forecast labor demand, state the rules a schedule must obey, choose an objective, and solve for the assignment of people to shifts. Apart from the runnable example near the top, it reports no measured results, and it draws on no customer data or experiments of our own.

Key Points

  • Two problems, in order: first forecast how much work arrives in each time slot, then assign people to shifts so that forecast demand is covered. Weak forecasts limit how good any schedule can be.
  • Constraints define the problem: coverage, availability, skills, rest rules, contract hours and local labor rules decide which schedules are allowed. Missing a rule produces schedules that look optimal and cannot be used.
  • Objectives are a choice: labor cost, understaffing penalties, overtime and employee preferences can all be weighted. The weights are a business decision the model makes explicit.
  • Several solution families exist: integer programming, constraint programming, heuristics and hybrids, each with different strengths on size and rule complexity.
  • Model upkeep matters: demand patterns and rules change, so forecasts and constraints need periodic review.

How to use this paper: read it as a guide to formulating and solving a scheduling problem, and to deciding whether the effort fits your situation. Any figures in it are arithmetic in a labelled example, not observed outcomes.

A Runnable Scheduling Example With Run Output

Question. For one made-up week in a small shop, what is the cheapest roster that covers every two-hour block, and how does the cost change if one person cannot work one day?

Inputs. Everything is typed into the script. There are seven days with six two-hour blocks each (8-10 up to 18-20), and a demand number for every block. There are four shift types: AM4 (blocks 1 and 2), PM4 (blocks 5 and 6), DAY8 (blocks 1 to 4) and LATE8 (blocks 3 to 6). Five employees have typed hourly wages (Ana 10, Ben 10, Cy 9, Di 12, Eli 11) and typed days they cannot work. The rest rule is no more than one shift per person per day. A shift costs its hours times the person's wage.

Method. Each choice of person, day and shift is a 0 or 1 variable. The model minimizes total cost. Each block must have at least as many people as its demand, and each person works at most one shift a day. The script solves it as an integer program with scipy.optimize.milp.

Result. The solver reports status 0, which means it finished with an optimal answer within the solver's default tolerance. The base week needs 24 shifts at a total cost of 1628. The model requires demand to be met in every block, and the solver found a roster that does so. The cost is the minimum, but other rosters can have the same cost. When Cy is also unavailable on Friday, the best roster still needs 24 shifts, and the cost rises by 20 to 1648. The 20 comes from the typed wages: Cy has the lowest wage, 9 an hour, so losing a Cy shift pushes work to higher-wage staff.

Limit. Demand, wages and availability are typed in, so the roster is optimal only for these typed inputs. The page does not forecast demand, and the example uses none of your data. It shows how the model is set up and solved, nothing more.

import numpy as np
from scipy.optimize import milp, LinearConstraint, Bounds

DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
BLOCKS = ["8-10", "10-12", "12-14", "14-16", "16-18", "18-20"]
DEMAND = {  # people needed in each 2-hour block, typed in
    "Mon": [1, 2, 2, 2, 1, 1],
    "Tue": [1, 2, 2, 2, 1, 1],
    "Wed": [1, 2, 3, 3, 2, 1],
    "Thu": [1, 2, 2, 2, 1, 1],
    "Fri": [2, 2, 3, 3, 2, 2],
    "Sat": [1, 2, 3, 3, 2, 2],
    "Sun": [0, 2, 2, 2, 1, 0],
}
# shift type: (first block, number of blocks), typed in
SHIFTS = {
    "AM4": (0, 2),
    "PM4": (4, 2),
    "DAY8": (0, 4),
    "LATE8": (2, 4),
}
# hourly wage per employee, typed in; a shift costs hours x wage
WAGE = {"Ana": 10, "Ben": 10, "Cy": 9, "Di": 12, "Eli": 11}
def shift_cost(e, s):
    return SHIFTS[s][1] * 2 * WAGE[e]
EMPLOYEES = ["Ana", "Ben", "Cy", "Di", "Eli"]
# days each employee can NOT work, typed in
OFF = {"Ana": ["Sun"], "Ben": ["Sat", "Sun"], "Cy": [], "Di": ["Mon", "Tue"], "Eli": ["Wed"]}

def solve(off):
    keys = [(e, d, s) for e in EMPLOYEES for d in DAYS for s in SHIFTS
            if d not in off[e]]
    n = len(keys)
    cost = np.array([shift_cost(e, s) for (e, _, s) in keys], dtype=float)
    rows, lo = [], []
    # coverage: people on shift in each block >= demand
    for d in DAYS:
        for b in range(len(BLOCKS)):
            row = np.zeros(n)
            for i, (e, dd, s) in enumerate(keys):
                start, length = SHIFTS[s]
                if dd == d and start <= b < start + length:
                    row[i] = 1
            rows.append(row)
            lo.append(DEMAND[d][b])
    # rest rule: at most one shift per employee per day
    hi_rows = []
    for e in EMPLOYEES:
        for d in DAYS:
            row = np.zeros(n)
            for i, (ee, dd, s) in enumerate(keys):
                if ee == e and dd == d:
                    row[i] = 1
            if row.any():
                hi_rows.append(row)
    A = np.vstack(rows + hi_rows)
    lb = np.array(lo + [0] * len(hi_rows), dtype=float)
    ub = np.array([np.inf] * len(rows) + [1] * len(hi_rows))
    res = milp(cost, constraints=LinearConstraint(A, lb, ub),
               integrality=np.ones(n), bounds=Bounds(0, 1))
    return keys, res

def report(title, off):
    keys, res = solve(off)
    print(title)
    print("solver status:", res.status, "-", res.message)
    if res.x is None:
        return None
    chosen = [k for k, v in zip(keys, res.x) if v > 0.5]
    for d in DAYS:
        print(" ", d, ", ".join(f"{e}:{s}" for (e, dd, s) in chosen if dd == d) or "(none)")
    cover = {d: [0] * len(BLOCKS) for d in DAYS}
    for (e, d, s) in chosen:
        st, ln = SHIFTS[s]
        for b in range(st, st + ln):
            cover[d][b] += 1
    met = all(cover[d][b] >= DEMAND[d][b] for d in DAYS for b in range(len(BLOCKS)))
    total = sum(shift_cost(e, s) for (e, _, s) in chosen)
    print("number of shifts:", len(chosen))
    print("total cost:", total)
    print("demand met in every block:", met)
    return total

base = report("Base week", OFF)
alt_off = dict(OFF)
alt_off["Cy"] = ["Fri"]
alt = report("\nVariant: Cy also unavailable on Fri", alt_off)
print("\nCost change:", alt - base if alt is not None and base is not None else "n/a")

Printed output from running the code above:

Base week
solver status: 0 - Optimization terminated successfully. (HiGHS Status 7: Optimal)
  Mon Ana:DAY8, Ben:AM4, Cy:LATE8
  Tue Ana:PM4, Ben:DAY8, Cy:DAY8
  Wed Ana:LATE8, Ben:DAY8, Cy:LATE8, Di:AM4
  Thu Ana:PM4, Ben:DAY8, Cy:DAY8
  Fri Ana:DAY8, Ben:DAY8, Cy:LATE8, Eli:PM4
  Sat Ana:LATE8, Cy:DAY8, Di:PM4, Eli:DAY8
  Sun Cy:DAY8, Di:PM4, Eli:DAY8
number of shifts: 24
total cost: 1628
demand met in every block: True

Variant: Cy also unavailable on Fri
solver status: 0 - Optimization terminated successfully. (HiGHS Status 7: Optimal)
  Mon Ana:PM4, Ben:DAY8, Cy:DAY8
  Tue Ana:LATE8, Ben:AM4, Cy:DAY8
  Wed Ana:DAY8, Ben:LATE8, Cy:LATE8, Di:AM4
  Thu Ana:DAY8, Ben:AM4, Cy:LATE8
  Fri Ana:DAY8, Ben:LATE8, Di:AM4, Eli:LATE8
  Sat Ana:LATE8, Cy:DAY8, Di:PM4, Eli:DAY8
  Sun Cy:LATE8, Di:AM4, Eli:DAY8
number of shifts: 24
total cost: 1648
demand met in every block: True

Cost change: 20

1. Introduction

1.1 Problem Statement

Workforce scheduling is the process of assigning employees to shifts, roles and locations so that expected demand is covered while a set of rules is respected. The number of possible schedules grows exponentially. As simple arithmetic: if each of 50 employees can take one of 4 options (three shifts or off) on each of 7 days, there are 4^350 combinations, roughly 10^210. No one can inspect that space by hand, which is why a method is needed to search it systematically.

Common manual approaches are manager judgment, spreadsheets, and rule-of-thumb software. They can work well for small teams with few rules. As rules, skills and locations multiply, it becomes harder to tell whether a schedule is good or merely acceptable, and an optimization model gives a way to state that question precisely.

1.2 Scope and Objectives

This paper explains the building blocks of scheduling optimization: demand forecasting for staffing, the types of constraints, common formulations, and the families of solution methods. It also covers assumptions, when the approach is a good fit, common pitfalls and practical guidance for applying it. It is a method guide. It does not benchmark vendors, quantify savings or describe any organization's results.

1.3 Why the Framing Is Useful

Writing a schedule as a model forces the rules, costs and priorities into the open. Disagreements that are usually settled informally, such as how much a late-night shift should weigh against a weekend shift, become explicit parameters that can be discussed and changed. The same model can also be re-solved when demand or availability changes, which makes what-if questions cheap to ask.

2. Background

2.1 Approaches to Scheduling

Scheduling practice ranges from fully manual rosters, to spreadsheets that track availability and check simple rules, to dedicated software that automates rule checking and notification, to systems that run an optimization solver. The distinction that matters for this paper is between methods that check whether a schedule is allowed and methods that search for the best allowed schedule under a stated objective.

2.2 Typical Limitations of Informal Methods

Informal methods tend to share several limitations, listed here as things to look for rather than as measured effects. Coverage may be uneven, with long stretches above and below the staffing that demand requires. Rules may be applied inconsistently, especially rest periods and contract limits. Scenario questions, such as what happens if three people are absent next week, are slow to answer. Forecasts, if they exist, may live apart from the schedule so that the two drift. Preferences and fairness are handled case by case.

2.3 Where Optimization Does Not Help

If the team is small, the rules are few and demand is steady, an optimization model may add more overhead than value. If demand data is missing or unreliable, a solver will faithfully optimize against a wrong forecast. If the real rules are unwritten or disputed, they cannot be encoded until they are agreed.

3. Method

This section describes the method itself. The paper does not run an experiment: it presents standard formulations and solution families from the operations research literature, plus one runnable example with its code and run output listed in the references.

3.1 Demand Forecasting for Staffing

Scheduling starts from a demand curve: how much work, in staff-hours or headcount, is needed in each time slot. Typical inputs are historical volumes by slot (for example by hour for a contact center, or by day-part for a store), calendar effects such as weekdays, holidays and promotions, and known events. Common forecasting approaches include seasonal averages, time series decomposition and regression with calendar variables. Whatever the technique, convert the volume forecast into required staff using a stated productivity or service-level assumption, and keep the forecast and the conversion separate so each can be checked.

Evaluate the forecast on held-out recent periods before building a schedule on it, and record the error by time of day, since errors at peaks are usually more costly than errors in quiet periods. Keep enough history to cover the seasonal cycle you care about; one full cycle is the minimum to see it at all.

3.2 Data Needed

  • Demand history at the slot size you will schedule on.
  • Employee data: availability, skills, contract hours, cost rates and, if used, preferences.
  • Rules: coverage minimums, maximum consecutive days, minimum rest between shifts, weekly hour limits, break rules, and local labor requirements. Many rules exist only in contracts or policy documents and must be translated into precise statements.
  • Outcome measures for monitoring: coverage against demand, overtime, schedule changes after publication, and employee-reported fit.

3.3 Constraint Types

  • Coverage constraints: required headcount, by skill, in each slot. Hard minimums or soft targets with a penalty for shortfall.
  • Individual constraints: availability, leave, maximum hours, minimum rest, maximum consecutive working days.
  • Skill and role constraints: only qualified people fill a role; some roles need a minimum count per shift.
  • Pattern constraints: allowed shift sequences, such as no day shift straight after a night shift.
  • Fairness constraints or objectives: balance of weekend, night or holiday shifts across people.
  • Hard versus soft: a hard constraint can never be broken; a soft constraint can be broken at a stated cost. Treating too many rules as hard can make the problem infeasible.

3.4 Optimization Formulations

A common formulation uses decision variables for which shift pattern each person works (or how many people work each shift pattern, for an anonymous staffing problem). The objective minimizes a weighted sum of terms such as wage cost, shortfall penalties, overtime premiums and preference violations. Constraints are linear inequalities over those variables. Set covering, shift-pattern and column generation models all follow this shape. Because assignments are whole people, the variables are integers, which makes the model a mixed-integer program.

3.5 Solution Approaches

Mixed-integer programming (MIP) uses a general solver and can prove how far a solution is from the best possible: the solver reports a gap between the best schedule found and a bound on the best possible. This is useful when you want to know how far a schedule can be from the best possible. It suits well-structured constraints and moderate size. Constraint programming (CP) expresses logical and sequencing rules naturally and is often good at finding any feasible schedule when the rules are tight. Metaheuristics such as local search, simulated annealing and genetic algorithms trade guaranteed quality for speed on very large or irregular problems. Hybrid approaches combine these, for example using CP to find a feasible roster and local search to improve it. No family is best in general; pick by problem size, rule complexity, and whether a quality bound matters. For the integer programming background see Wolsey, Integer Programming (1998).

4. Worked Example (Illustrative)

Illustrative example with made-up numbers. Nothing below was measured. It only shows how a coverage model is set up and solved.

Suppose a service desk needs the following staff in four consecutive 4-hour blocks: block 1 needs 4, block 2 needs 8, block 3 needs 6 and block 4 needs 3. Suppose there are three 8-hour shifts: shift A covers blocks 1 and 2, shift B covers blocks 2 and 3, and shift C covers blocks 3 and 4. Suppose each shift costs the same, so the goal is to use as few shifts as possible.

Let x_A, x_B and x_C be the number of people on each shift. The model is: minimize x_A + x_B + x_C subject to x_A ≥ 4 (block 1), x_A + x_B ≥ 8 (block 2), x_B + x_C ≥ 6 (block 3) and x_C ≥ 3 (block 4).

Block 1 forces x_A ≥ 4 and block 4 forces x_C ≥ 3. Block 2 then needs x_B ≥ 8 − x_A. Taking x_A = 4 gives x_B = 4, and block 3 then needs x_C ≥ 2, which block 4 already exceeds, so x_C = 3. The total is 4 + 4 + 3 = 11 shifts. Coverage by block is 4, 8, 7 and 3 against demands of 4, 8, 6 and 3, so block 3 is one person over. Because x_A + x_B ≥ 8 and x_C ≥ 3, no solution can use fewer than 11, so 11 is the minimum for this example. Other splits, such as 5, 3, 3, also total 11, which shows that the best schedule is often not unique, and a secondary objective (for example preferences) can choose among ties.

A real problem adds many more variables and rules, but the structure is the same.

5. Applying the Method

5.1 When to Use It

Optimization is a good fit when there are many people or locations, several overlapping rules, uneven demand through the day or week, and frequent re-planning. It is a weaker fit for small, stable teams where a manager can hold the whole problem in mind.

5.2 Assumptions to State Up Front

  • The demand forecast is a fair estimate of the needed staffing, and its error is known.
  • Productivity per person per slot is stable enough to convert volume to headcount.
  • The encoded rules are the real rules, including the unwritten ones that managers apply.
  • Employees can take the assigned shifts as published; last-minute absences are handled by a separate process.

5.3 Common Pitfalls

  • Optimizing a poor forecast: the schedule can only be as good as the demand estimate behind it.
  • Missing rules: a schedule that violates an unmodelled rule will be edited by hand, undoing the optimization.
  • Too many hard constraints: the model reports no feasible schedule. Soften the least important rules and give them a penalty.
  • Opaque objectives: if people cannot see why a schedule looks the way it does, they will distrust it. Report the objective terms.
  • One-time setup: rules and demand change, so forecasts and constraints need periodic review.

5.4 Technical Considerations

Match the solver to the problem. Tightly constrained, sequence-heavy problems often suit constraint programming; cleaner structures with a cost objective often suit MIP; very large or irregular problems may need heuristics. Plan for integration with the systems that hold time records, employee data and payroll rules, and for data checks so bad inputs are caught before they reach the solver.

6. Recommendations

These are general guidance for anyone applying the method. They are not conclusions drawn from a study.

Guidance 1: Write the rules down first

List every rule that shapes a schedule, mark each as hard or soft, and have the people who own the rules agree to the list. Gathering the rules can take a lot of effort, and missing rules can lead to schedules people cannot use.

Guidance 2: Start with one well-bounded scheduling area

Pick a single team or site with clear rules and usable demand history. Compare the model's output with the schedule produced by the current process on the same demand, and review the differences with the people who know the operation.

Guidance 3: Measure the forecast separately from the schedule

Track forecast error by time of day. If coverage problems trace back to forecast error, improving the solver will not fix them.

Guidance 4: Make the objective visible

Show the cost, shortfall, overtime and preference terms for each schedule so reviewers can see the trade-offs and adjust the weights deliberately.

Guidance 5: Review the model on a schedule

Set a regular review of forecasts, constraints and weights, and after any change in contracts, labor rules or the operation itself.

7. Conclusion

This paper explained a method: forecast staffing demand, state the constraints, choose an objective, and solve for the assignment of people to shifts using integer programming, constraint programming, heuristics or a hybrid. It is not a study. Apart from the runnable example near the top, it reports no measured results. The worked example uses made-up numbers to show the mechanics only.

Whether the method is worth the effort for a given organization depends on its size, the number and complexity of its rules, and the quality of its demand data. The practical starting points are to write the rules down, check the forecast on its own, and compare the model's schedule against the current one on the same demand before relying on it.

Your turn

Bring your own data and the question you actually need answered.

MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai

Frequently Asked Questions

What is workforce scheduling optimization?

It is the practice of expressing shift assignment as a mathematical model, with demand to cover, rules to obey and an objective such as cost, and then using a solver to find a schedule that satisfies the rules and scores well on the objective.

How does an optimization model differ from checking a manual schedule?

A checker tells you whether a given schedule breaks a rule. An optimization model searches among allowed schedules for one that scores best on a stated objective, and can be re-run when demand or availability changes.

What data does it need?

Demand history at the slot size you schedule on, employee availability, skills, contract hours and cost rates, the written and unwritten scheduling rules, and measures such as coverage and overtime to monitor the result.

How should the result be evaluated?

Compare the model's schedule against the current process on the same demand, using measures you define in advance, such as coverage against demand, overtime hours, and the number of manual edits needed. Set the baseline before changing anything.

What commonly goes wrong?

Typical problems are an unreliable demand forecast, rules that were never encoded, too many hard constraints leaving no feasible schedule, objectives that nobody can interpret, and models that are not reviewed after rules or demand change.

References and Further Reading

Academic Literature

  • Van den Bergh, J., Beliën, J., De Bruecker, P., Demeulemeester, E., & De Boeck, L. (2013). Personnel scheduling: A literature review. European Journal of Operational Research, 226(3), 367-385.
  • Ernst, A. T., Jiang, H., Krishnamoorthy, M., & Sier, D. (2004). Staff scheduling and rostering: A review of applications, methods and models. European Journal of Operational Research, 153(1), 3-27.
  • Bruecker, P. D., Van den Bergh, J., Beliën, J., & Demeulemeester, E. (2015). Workforce planning incorporating skills: State of the art. European Journal of Operational Research, 243(1), 1-16.