Scheduling is an interesting optimization problem that we encounter every day. You schedule things like brushing your teeth, driving to work, and eating a meal, whether consciously or subconsciously. I’m sure most people (me included) don’t care to fully optimize their personal schedule in favor of adding a buffer between tasks. Someone who is trying to optimize their schedule is in for a monumental task.

This type of scheduling problem is similar to an NP-hard problem in computer science, sometimes called the “Job-Shop Scheduling” problem. NP-hard problems are problems in which new inputs increase complexity combinatorially. In other words, adding a new task to your schedule would greatly increase the time required to fully optimize it. And that is the key piece of information here: fully optimize. What benefit do you actually get from having the most streamlined schedule if you have to schedule 2 hours a day optimizing your schedule tomorrow?

The book outlines 3 ways to optimize scheduling without extreme planning:

  1. Earliest Due Date
  2. Shortest Processing Time
  3. Hodgson-Moore Algorithm (which I’ll explain later)

First, let’s start with a simple scheduling problem: 4 tasks of varying lengths and due dates. I say “simple,” but actually this is also an NP-hard problem, a variant of the “Single-machine scheduling” problem with due dates. Even though each task is independent and the only constraint is due date, optimizing tasks completed makes it more complex.

Earliest Due Date

This simple algorithm just tells you to work on whatever task is due the soonest. Makes sense, this is usually what I do.

Suppose it’s January 5th and we have these 4 tasks:

  • Task 1 takes 3 days and is due on Jan 8th
  • Task 2 takes 2 days and is due Jan 11th
  • Task 3 takes 4 days and is due on Jan 12th
  • Task 4 takes 1 day and is due on Jan 14th

The following Gantt chart shows that if we just work on the tasks by the earliest due date first, then 2 tasks are late:

  gantt
    title Scheduling (Earliest Due Date)
    dateFormat YYYY-MM-DD
    axisFormat %b-%d

    section Tasks
        Task 1 (Due 1/8)  :t1, 2026-01-05, 3d
        Task 2 (Due 1/11) :t2, after t1, 2d
        Task 3 (Due 1/12) :crit, t3, after t2, 4d
        Task 4 (Due 1/14) :crit, t4, after t3, 1d

Working on the next due task falls short when there just isn’t enough time to complete all tasks. It can even snowball into a crisis if an early task is late, causing all other tasks to be late.

Shortest Processing Time

This algorithm is also simple, working on the smallest tasks first. Completing many tasks in a short amount of time will definitely feel productive in the beginning, but it is short-sighted, as we can see from the updated Gantt chart:

  gantt
    title Scheduling (Quickest First)
    dateFormat YYYY-MM-DD
    axisFormat %b-%d

    section Tasks
        Task 1 (Due 1/8)  :crit, t1, after t2, 3d
        Task 2 (Due 1/11) :t2, after t4, 2d
        Task 3 (Due 1/12) :crit, t3, after t1, 4d
        Task 4 (Due 1/14) :t4, 2026-01-05, 1d

In this case, we still only complete 2 tasks, even though they are the smallest.

Hodgson-Moore Algorithm

With this algorithm, begin with the earliest due date, but if it looks like we won’t get to any item before the due date, throw out the largest item so far (either the largest item before it or itself).

In this Gantt chart, you can see that 3 tasks are now completed instead of only 2:

  gantt
    title Scheduling (Moore's Algorithm)
    dateFormat YYYY-MM-DD
    axisFormat %b-%d

    section Tasks
        Task 1 (Due 1/8)  :t1, 2026-01-05, 3d
        Task 2 (Due 1/11) :t2, after t1, 2d
        Task 3 (Due 1/12) :crit, t3, after t2, 4d
        Task 4 (Due 1/14) :t4, after t2, 1d

This works because we’re simply removing the tasks with the greatest impact when needed, leaving more time to do other tasks. It is slightly more work than the first two algorithms, but it can greatly optimize a schedule compared to blindly picking the next task based on a single metric.

Software Project Scheduling

If you’re in the software space or a domain with similar dynamic scheduling, you might be laughing at these algorithms because of how useless they would be to your task planning. This type of scheduling is difficult because of the many factors that each add unknown delays:

  • Tasks might have dependencies not known before starting
  • Each team member has different knowledge, in the specific domain and in general that would make a task faster or slower
  • New team members need time to gain knowledge of the domain, usually taking time from existing members
  • Task switching (e.g. between development and meetings) adds a larger development delay than the length of the meeting. There is a minimum amount of time to make any progress on a task, with development it is usually very high
  • Bugs will come up as well, which necessarily can’t be predicted
  • And of course, the whack-a-mole problem, where resolving one issue creates another issue somewhere else

All of these difficulties can be dealt with by thinking about tasks in a different way, using Agile (Scrum, Kanban, etc.). Instead of thinking in due dates, think in priorities. Due dates are static, priorities are dynamic. If there’s some feature in your planned product that doesn’t add much value, that is a low priority and should be worked on later. If there’s a bug that surfaces which impacts your main product functionality, that is a high priority and should be worked on first.

Priorities need to be assessed as continuously as needed, then the team can add estimated times to tasks. Once you’ve done that, you can project estimated completion dates for tasks, but it’s not set in stone. The main idea with Agile is that no task should be considered unless it’s clear that it will bring value. This is accomplished through communication and responding to change.