The critical path method (CPM) finds the longest sequence of dependent activities in a project plan. Its length is the shortest possible completion time under the planned durations and dependencies. A delay on that path delays the project; activities off the path may have scheduling flexibility. Use CPM to decide which work needs the closest attention and which activities could shorten the schedule if accelerated. section 13.3
Start with a work breakdown structure that identifies activities and their predecessors, then assign each activity an estimated duration. Calculate four times for every activity:
- Forward pass: earliest start (ES) is zero when there are no predecessors; otherwise it is the greatest predecessor earliest finish. Earliest finish (EF) = ES + duration. section 13.3.1
- Backward pass: begin at the project finish. Latest finish (LF) is the smallest latest start among immediate successors; latest start (LS) = LF − duration. section 13.3.2
- Slack = LS − ES, equivalently LF − EF. Zero-slack activities are critical; positive slack indicates how far an activity can move without delaying project completion. section 13.3.3
Automated reporting tool: 29 weeks
The plan contains 12 activities whose expected durations sum to 39 weeks, but some work can proceed in parallel. CPM gives a 29-week project duration and the critical sequence A → B → C → D → E → G → J → K → L. Activities F and H each have five weeks of slack; I has nine. A small Python implementation can calculate the schedule from activities entered in dependency order: ```python tasks = { 'A': (3, []), 'B': (2, ['A']), 'C': (3, ['A','B']), 'D': (5, ['C']), 'E': (3, ['D']), 'F': (4, ['C']), 'G': (4, ['E']), 'H': (3, ['F']), 'I': (3, ['F']), 'J': (4, ['G','H']), 'K': (3, ['I','J']), 'L': (2, ['K']) } es, ef = {}, {} for task, (duration, predecessors) in tasks.items(): es[task] = max((ef[p] for p in predecessors), default=0) ef[task] = es[task] + duration finish = max(ef.values()) successors = {task: [] for task in tasks} for task, (_, predecessors) in tasks.items(): for predecessor in predecessors: successors[predecessor].append(task) ls, lf = {}, {} for task in reversed(tasks): lf[task] = min((ls[s] for s in successors[task]), default=finish) ls[task] = lf[task] - tasks[task][0] critical = [task for task in tasks if ls[task] == es[task]] print(finish, critical) # 29 ['A', 'B', 'C', 'D', 'E', 'G', 'J', 'K', 'L'] ``` The input order matters here: every predecessor must appear before its dependent activity. Check that ordering and the estimates before relying on the result. section 13.1 section 13.3.3 section 13.3.4
Table 13.3 CPM results for the automated reporting tool project. Activities in any project have slack or no slack, which is derived by subtracting the earliest start from the latest start or, alternatively, by subtracting the earliest finish from the latest finish (it doesn’t matter). Activities with no slack are on the critical path, whereas activities with slack are not.
|
Activity
|
Earliest start (ES)
|
Earliest finish (EF)
|
Latest start (LS)
|
Latest finish (LF)
|
Slack (LS – ES)
|
Critical path?
|
|---|---|---|---|---|---|---|
|
A
|
0
|
3
|
0
|
3
|
0
|
Yes
|
|
B
|
3
|
5
|
3
|
5
|
0
|
Yes
|
|
C
|
5
|
8
|
5
|
8
|
0
|
Yes
|
|
D
|
8
|
13
|
8
|
13
|
0
|
Yes
|
|
E
|
13
|
16
|
13
|
16
|
0
|
Yes
|
|
F
|
8
|
12
|
13
|
17
|
5
|
No
|
|
G
|
16
|
20
|
16
|
20
|
0
|
Yes
|
|
H
|
12
|
15
|
17
|
20
|
5
|
No
|
|
I
|
12
|
15
|
21
|
24
|
9
|
No
|
|
J
|
20
|
24
|
20
|
24
|
0
|
Yes
|
|
K
|
24
|
27
|
24
|
27
|
0
|
Yes
|
|
L
|
27
|
29
|
27
|
29
|
0
|
Yes
|
Compare each activity’s earliest and latest times to see exactly which activities have zero slack and which can move within the 29-week schedule. section 13.3.3
A 29-week schedule is an estimate, not a guarantee of finishing in 29 weeks: activity durations vary. Deadline-probability analysis uses variance estimates for critical activities. To shorten the planned schedule, examine critical activities rather than spending money accelerating noncritical ones, and recalculate the path when durations change. section 13.4 section 13.5
