How do you estimate honestly, cut scope, and sequence a large migration: ranges, the critical path, vertical slices and risk-first ordering?
Predict first
A task estimated at 2, 4 and 12 days, and a plan with dependencies. What are the three-point estimate, the critical path and the slack of the documentation task?
func pert(_ optimistic: Double, _ likely: Double, _ pessimistic: Double) -> Double {
(optimistic + 4 * likely + pessimistic) / 6
}
print("PERT(2, 4, 12):", pert(2, 4, 12), "days")
let tasks: [String: (days: Int, needs: [String])] = [
"api": (5, []), "schema": (3, []), "adapter": (4, ["api", "schema"]),
"ui": (6, ["adapter"]), "docs": (2, ["api"]), "rollout": (3, ["ui", "docs"]),
]
var memo: [String: Int] = [:]
func finish(_ name: String) -> Int {
if let known = memo[name] { return known }
let value = tasks[name]!.days + (tasks[name]!.needs.map(finish).max() ?? 0)
memo[name] = value
return value
}
func criticalPath(_ name: String) -> [String] {
let needs = tasks[name]!.needs
let slowest = needs.max { finish($0) < finish($1) }
return (slowest.map(criticalPath) ?? []) + [name]
}
print("critical path:", criticalPath("rollout").joined(separator: " -> "), "\(finish("rollout")) days")
let rolloutStarts = max(finish("ui"), finish("docs"))
print("docs can slip \(rolloutStarts - finish("docs")) days without moving the end")
- PERT 5.0 days; critical path api, adapter, ui, rollout for 18 days; docs can slip 8 days
- PERT 4 days; critical path includes docs
- PERT 12 days; no slack
Mental model. Estimating a trek you have never done, you give a range and revise it when you see the first hill. The critical path is the one trail that has no shortcut; the scenic detours can wait.
Estimate in ranges, not points, because uncertainty is the real content of an estimate. Ask for an optimistic, a most likely and a pessimistic figure; the classic three-point (PERT) estimate is (optimistic + 4 × likely + pessimistic) / 6, so 2, 4 and 12 days give 5, noticeably above the “likely” 4 because the long tail is real (the check below). Use reference classes (how long did similar work really take), split work until pieces are small enough to estimate, include testing, review, rollout and the work that is not code, and state what you do not know. Uncertainty narrows as you learn (the cone of uncertainty), so give a wide range early and tighten it after a spike. Communicate ranges and confidence (“three to five weeks, with the third-party SDK the main risk”) and update them as facts change, rather than defending a number.
To sequence, draw the dependencies and find the critical path: the longest chain of dependent tasks, which sets the earliest finish. In the example the path is the API, the adapter, the UI and the rollout for 18 days, while the documentation has slack and can slip eight days without moving the end; effort belongs on the critical path, and adding people to a task that is not on it does not help. Order work by risk first: do the unknown, high-impact piece early (a spike, a walking skeleton that runs end to end in thin form) so you learn while you can still change the plan. Deliver in vertical slices (one complete user journey through every layer) rather than horizontal layers, so value and feedback arrive early and each slice can ship behind a flag (Q486). For a large migration use the strangler pattern with adapters and characterisation tests (Q357), migrate by feature or module with clear owners, keep both paths working until the old one is measured unused, and keep the app shippable at every step. To cut scope, rank by user value and risk (must, should, could), cut features not quality, and move the date only after trading scope. Track progress by finished slices and remaining risk, not by percent complete.
The estimate is weighted toward the likely value but pulled up by the tail, and only the longest dependency chain controls the end date.
Common traps
- A single-point estimate treated as a promise. It hides the uncertainty and turns the first slip into a failure.
- Building horizontal layers first. Nothing works end to end until late, and the riskiest part is discovered last.
They might follow up with
- What is the critical path? The longest chain of dependent tasks; any delay on it delays the finish.
- What is a walking skeleton? A thin end-to-end implementation that proves the architecture before details are added.
Related questions
- 26.4.1 How do you write and run an RFC, record decisions as ADRs, set technical strategy, and decide between build…
- 26.4.3 How do you run an engineering incident, hold a blameless review, and mentor and hire well: roles, mitigation…
- 26.4.4 How do you think about a platform organisation and multi-year technical bets: charter, paved road, build…
- 15.3.2 How do you migrate a large legacy codebase safely: the strangler pattern, adapters and characterization tests?
- 25.4.1 How do you design feature flags and remote config for a mobile app: flag kinds, defaults, stable percentage…
← How do you write and run an RFC, record decisions as ADRs, set… How do you run an engineering incident, hold a blameless review, and… →