PuLP is a Python library for writing optimization models. MILP stands for Mixed Integer Linear Program. CBC is an open-source solver. Together, they’re what makes the Staff Scheduler work — and together, they represent something I find genuinely interesting: the fact that problems that used to require expensive commercial software and specialized hardware can now be solved on a laptop, for free, in a few seconds.

Let me explain what’s actually happening under the hood.
The problem in plain English
The staff scheduling problem sounds simple until you write it down. You have a training company. Clients need to be taught courses — Excel, PowerPoint, Project Management, that sort of thing. Staff members teach the courses. Each staff member is available certain hours on certain days. Each client is available certain hours on certain days. Some courses require shared equipment (one projector, one workstation with specific software). Some courses have to happen at the exact same time for multiple clients — a group demo, a joint Q&A session.
The question: who teaches what, to whom, on which day, at which hour?
With five staff members and ten clients and a week of hourly slots, the number of possible assignments is astronomical. You can’t try them all. You need a smarter approach.
What a Mixed Integer Linear Program actually is
A Mixed Integer Linear Program is a way of framing a decision problem mathematically. You define decision variables — things you’re trying to decide — and you specify an objective (something to maximize or minimize) and constraints (rules the solution has to follow).
For the staff scheduler, the decision variables are binary: for every possible combination of (staff member, day, time slot, client, course), is this assignment made or not? One means yes, zero means no. The solver’s job is to find the combination of ones and zeros that satisfies all the constraints and maximizes the objective.
The objective in this model is to maximize total assignments, with two penalty terms to make the schedule look reasonable. The first penalty discourages spreading a single course across many different time slots — ideally, Excel Basic always happens at 10am, not scattered across the week. The second penalty discourages staff members from working isolated scattered hours — better to have a block of teaching than a single slot here and a single slot there.
The constraints cover the real-world rules. A staff member can only teach one class at a time. A client can only have one session at a time (with an exception for simultaneous courses, which I’ll get to in the next post). Each client-course-staff combination has an exact number of required weekly slots, and the model has to meet it exactly. Shared equipment creates capacity limits. No staff member works more than three consecutive hours.
Why CBC is a big deal
The solver — the thing that actually searches through all those possible assignments and finds the best one — is called CBC, which stands for COIN-OR Branch and Cut. It’s open source. It ships with PuLP via a single pip install command. It solved the sample scheduling problem to proven optimality in seconds.
Twenty years ago, this kind of problem required CPLEX or Gurobi. Those are commercial solvers with commercial licenses. A CPLEX license for a single user can run well over $10,000 a year. Gurobi is similar. Large companies bought them because there was no alternative if you needed to solve integer programs reliably.
CBC changed that. It’s not as fast as the commercial solvers on the hardest problems, but for problems of this size — a training company’s weekly schedule, a routing problem for a small fleet, a warehouse slotting exercise — it’s more than good enough. The fact that it exists, and that it’s free, and that it installs in seconds, is genuinely impressive. (I worked in environments where the procurement process for a CPLEX license took longer than building the model. CBC removes that entire obstacle.)
Where PuLP fits in
PuLP is the glue between your Python code and the solver. You write your model in Python — define variables, write out the objective and constraints in mathematical notation — and PuLP translates it into a format the solver understands, runs CBC, and hands you back the solution.
The original version of this model ran inside Excel via SolverStudio, a plugin that embeds PuLP code directly in a spreadsheet. SolverStudio reads data from named sheets and passes it to the model automatically. It works well — I’ve been using it for years — but it has an obvious limitation: the model is invisible and inaccessible to anyone who doesn’t have SolverStudio installed, which is essentially everyone.
The standalone version reads the same Excel workbook, builds the same model, calls CBC through PuLP, and returns the same result. The math is identical. The difference is that now anyone with a browser can run it.
What the output looks like
On the sample data — seven staff members, two clients, about a dozen courses — the model produces 86 assignments. Status: Optimal. That means the solver has proven there is no better solution. It checked, and this is as good as it gets.
The result comes back as a schedule: Staff / Day / Slot / Client / Course. The web app displays it as a pivot table (time slots on rows, days on columns, assignment counts in the cells — filterable by staff, client, or course) and lets you download it as a CSV.
You can see it for yourself: try the Staff Scheduler here with the sample workbook.
The next post is about the bugs — three specific failures that came up during the conversion from Excel to standalone Python, all of which would have stayed invisible in the original spreadsheet. That’s the honest part of this story.
Not every optimization post here is heavy — I once did the math on how many days five months actually is.
Things that I use, like, and am affiliated with:
Mint Mobile offers great cell phone service for $15 flat, get $15 off using the link. Get discounted phones with service activation and no contract.
I never spend money before I check Mr Rebates or Rakuten to get cashbacks, rebates, discounts, coupons or cheaper gift cards.
