Skip to content

Spreading, not cramming: how a planner should actually schedule your week

· 7 min read

Say you have twelve pieces of work, each with an estimate and a deadline, and five focused hours a day. You want software to tell you what to do today.

There is an obvious algorithm, it is the one most people reach for, and it produces a plan nobody uses. Worth understanding why, because the fix is small and the difference in outcome is total.

The obvious algorithm, and why it fails

Sort by deadline. Walk forward through the days. Fill each day to capacity from the top of the list.

This is greedy earliest-deadline-first, and it is optimal in the narrow sense that if any schedule meets all the deadlines, this one does too. That is a real theorem and it is why the approach is tempting.

It also produces this, on a Monday:

Today — 5 hours
  Tender response        2h 00m   (due Friday)
  Q3 board pack          1h 30m   (due 24 September)
  Website copy review    1h 00m   (due 3 October)
  Insurance renewal      0h 30m   (due 12 October)

Look at the last two lines. Work due in three and five weeks is on today's list, because there was room in today and it had to go somewhere.

That is where the plan dies. The user sees things that obviously don't need doing today, concludes the tool doesn't understand their job, and stops opening it. Being provably optimal is no help at all if the output isn't believed.

There is a subtler failure too. Greedy filling front-loads everything, so early days are packed and later days are empty. The plan is brittle: one bad morning and the whole cascade shifts, because there was no slack anywhere near the work that needed it.

The fix: ask for a share, not a slot

Instead of a task taking as much of today as it can get, let each task ask for the share of today proportional to how much of its remaining runway today represents.

A ten-hour task with ten working days left asks for one hour. Tomorrow it asks for one hour again. If it gets neglected for three days, the remaining nine hours are spread over seven days and it starts asking for one hour twenty — the ask grows as the runway shrinks, which is exactly the pressure you want.

The same Monday:

Today — 3 hours 25 minutes
  Stand-up               0h 15m   (fixed, 09:00)
  Tender response        1h 30m   (due Friday, 6h left)
  Q3 board pack          1h 00m   (due 24 September, 12h left)
  Almadar reply          0h 40m   (due tomorrow, 40m left)

Shorter, obviously sensible, and finishable. And finishing it genuinely means everything is still on track, which is the property that makes a list worth opening tomorrow.

Four rules that keep it honest

Proportional sharing alone isn't enough. Four constraints turn it into something that survives contact with a real week.

1. Fixed commitments come off the top. A stand-up every weekday at 09:00 is not something the planner gets to move. Recurring work is subtracted from the day's capacity before any deadline work is considered. Any planner that treats a standing meeting as movable is a planner that has never been used.

2. Nothing under thirty minutes. Proportional sharing across many tasks produces crumbs: eight minutes here, twelve there. Eight minutes of a four-hour job is not work, it is a context switch. Below a floor, the slice is dropped and the time goes to the next task.

3. A task always gets what it must have today to remain possible. This is the rule that overrides the share. If a task needs three hours today or it can no longer be finished at all, it gets three hours regardless of what its proportional share said. This is what drags neglected work forward as the deadline closes in, and it is what stops "spread" quietly becoming "delay".

4. Aim to finish early. Work targets completion slightly before its deadline — say fifteen per cent of the runway, capped at a few days. It costs almost nothing in daily minutes and it means one bad day doesn't become a missed date.

Feasibility is a separate question, and must be answered exactly

Here is the part most tools get wrong, and it matters more than the scheduling.

If the plan is oversubscribed, is that a real problem or an artefact of how the plan was laid out?

You cannot answer that from the schedule. A schedule is one arrangement of many; a task overflowing its day might mean genuine overload, or it might mean the algorithm put it in an awkward place. Warning on the basis of the schedule produces false alarms, and a warning system with false alarms is worse than none — people learn to dismiss it, including the true one.

So feasibility should be decided independently and exactly. For one person starting now, a set of deadlines is achievable if and only if, for every deadline, the work due by then fits in the hours available by then. Check every deadline; if any fails, that is a real capacity problem with a real number attached, and no rearrangement of the schedule can fix it.

That is what makes an "at risk" flag trustworthy. It is never an artefact. When it fires, the only three fixes are more hours, a later date, or less work — and the tool can say which one, and by how much.

Working backwards, for slack

One more pass earns its keep. Run the mirror image of the forward pass: latest deadline first, each task filling backwards from its own deadline. That gives every task its latest responsible start — the last day it can be begun without becoming impossible.

Now "slack" is a real number rather than a feeling, and a task can honestly be described as tight when it can no longer be put off. It is also what lets you answer "what can I drop this week?" without guessing.

The general lesson

The failure of greedy filling is not that the algorithm is wrong. It is that it optimises the wrong thing. It answers "can these deadlines be met?" when the user asked "what should I do today?"

Those are different questions and they want different algorithms. Answer the second with spreading, answer the first exactly and separately, and show both. Answer only the first and you get a technically optimal plan that is closed and never reopened.


The planner described here is otoodo's. It lives in one directory as pure functions — no database, no framework — under about seven hundred lines of tests, because a scheduler you cannot check is a scheduler you cannot trust.