Gantt Dependencies Explained: FS, SS, FF, SF and Lead/Lag
Published
A dependency is a promise about time between two tasks. The start or the finish of the predecessor constrains the start or the finish of the successor — four combinations, and their names are FS, SS, FF, and SF. Add lag and lead to express waiting or overlap, and you can write down nearly all of a project's schedule logic.
What a dependency promises
Why does getting these four right matter? A dependency is not decoration on an arrow; it is the input to critical path calculation. Wire a link wrong and the math still runs, faithfully and confidently, to the wrong answer.
| Dependency | How to read it | When to use it |
|---|---|---|
| FS (Finish-to-Start) | Predecessor must finish before successor starts | A deliverable has to change hands before the next work can begin (the default; most of real life) |
| SS (Start-to-Start) | Predecessor must start before successor starts | Parallel work with an order of entry (test cases begin once development is under way) |
| FF (Finish-to-Finish) | Predecessor must finish before successor finishes | Work that has to land together (code review can't close before development does) |
| SF (Start-to-Finish) | Predecessor must start before successor finishes | Handover and cutover (the new system going live ends the old system's operation) |
FS — the default, and most of real life
The most natural form. "Development starts once the design is approved." The point where a deliverable changes hands becomes the link.
Making FS your default is a good strategy. Reach for the other three only when FS can't express the relationship. Open the dependency list of a real project and eight or nine out of ten being FS is healthy. If FS has fallen below half, you are almost certainly forcing parallel work into SS links or over-linking in general.
SS — order of entry for work that runs alongside
"Once development starts, test case authoring can start too." The two run in parallel, but there is an order to starting. Wire this as FS (test cases start only after development finishes) and you push the schedule out for no reason.
One trap to know: SS does not constrain the finish. If development runs three months and test case authoring takes two weeks, SS alone permits the test cases to finish two months early without violating the logic. If what you mean is "start together and finish together," you need both SS and FF.
FF — work that has to land together
"Code review cannot finish before development finishes." Review starts mid-development, but you can't close the review with the last commit unreviewed. The successor's finish is tied to the predecessor's finish.
Work that shadows a main task — audits, inspections, documentation — is usually FF. Wire it as FS (inspection starts only after the work is fully done) and you stretch genuinely parallel work into a long serial chain on the schedule.
SF — rare, but there is a seat for it
"When the new system starts running, the old system's operation ends." The successor's finish (old system operation ending) is tied to the predecessor's start (new system going live).
SF is rare because it runs against the direction people think. We normally reason front to back — "we take the old system down, then we bring the new one up." So the places SF genuinely fits are mostly handover and cutover: system transitions with a parallel-run window, staff rotations, returning leased equipment.
Practical advice: before reaching for SF, re-examine your task definitions. Model it as "old system shutdown (a task)" instead of "old system operation (a state)" and it becomes an ordinary FS. Half the places SF seems necessary dissolve when you change the modeling.
Lead and lag — tell them apart by meaning, not by sign
Lag is waiting laid on top of a link. "Seven days from contract signature to payment before kickoff" — nobody is working, but time has to pass. Write it FS +7d.
Lead is overlap. "Start development when the design is about 80% done" becomes FS −5d.
The two are implemented as positive and negative values on the same axis, but they mean different things, so it's safer to think of them separately. Three rules:
- Express waiting as lag, never as a fake task. Turn "waiting 7 days for payment" into a 7-day-duration task and you pollute progress rollups and effort totals. Time when nobody works is not work.
- Lead is a statement about risk. Starting development before the design is settled means accepting the possibility of rework. A schedule full of leads is not a fast schedule; it is a risky one.
- Never substitute a pinned start date for lag. The result looks the same until the predecessor slips — then lag moves with it, and the pinned date stays put and breaks the logic.
Get it wrong and the critical path lies to you
The cost of a dependency error is not immediately visible. The calculation completes normally and a perfectly plausible critical path appears on screen. The trouble is that the answer is wrong.
Three signature symptoms:
- A link is missing → float reads larger than it really is. You conclude "this task has 15 days of slack, being late is fine" — and the moment the missing link surfaces, the slack is zero. We hit exactly that scene on a real project and wrote it up in If One Task Slips Three Days, How Late Is the Project? — we fed in a three-day slip, the total delay came back as zero days, and that zero was the problem.
- Links are overused → genuinely parallel work gets chained serially and the critical path stretches. The schedule inflates by itself.
- Dates are pinned → pin a start date instead of drawing a link and the successor won't move when the predecessor slips. The math says "no delay" while reality collapses.
The third is the most common and the most dangerous. It is also close to inevitable when the WBS and the Gantt live in separate files (A Plan Made in Excel Starts Dying the Day It's Born).
Choosing the right one
Say the relationship between the two tasks out loud in one sentence, then pick from the table.
| If you say it this way | Answer |
|---|---|
| "B needs A's deliverable" | FS |
| "Once A starts, B can start too" | SS |
| "B cannot finish before A finishes" | FF |
| "A and B start together and finish together" | SS + FF (both) |
| "When A switches on, B switches off" | SF |
| "B starts a few days after A finishes" | FS + lag |
| "Start B early, as A is wrapping up" | FS + lead (accepting rework risk) |
| "B starts March 1, no matter what" | Not a dependency — that's a constraint; check whether you really need it |
That last row is the most abused in practice. Keep constraints for dates that are genuinely fixed — external contracts, statutory deadlines — and express everything else as links. A schedule full of constraints is a schedule that cannot be calculated.
Where to go next
Before you can wire dependencies, the task list itself has to be right. What to decompose, and how, is laid out in five steps in How to Create a WBS.
wbsgantt supports all four types — FS, SS, FF, SF — plus lead and lag, blocks cycles inside the transaction at the moment you add a link, and recomputes the critical path automatically. A task whose float has dropped to zero is flagged in its detail panel right away — the goal being that nobody has to hunt for the first symptom above (float overstated because a link is missing) by eye.
Frequently asked questions
- When is SF actually used?
- Only for handovers and cutovers — a go-live whose start ends the old system's operation, a staff rotation, returning leased equipment. That said, half the places SF seems necessary disappear when you redefine the task: model "old system operation" as "old system shutdown" and it becomes an ordinary FS.
- What's the difference between lead and lag?
- Lag is waiting laid on the link (FS +7d — payment terms, where nobody is working). Lead is overlap (FS −5d — starting the successor before the predecessor finishes). They are the same axis with opposite signs, but different in meaning: a schedule full of leads is not a fast schedule, it is one that has accepted rework risk.
- Can I just pin dates instead of linking tasks?
- The results look identical until the predecessor slips, and then they behave in opposite ways. A linked successor moves with the slip and the delay becomes visible; a pinned date stays put and reports "no delay," which is false. Keep pinned dates for genuinely fixed constraints — contracts, statutory deadlines — and express the rest as links.
- What happens if dependencies form a cycle?
- With a cycle (A→B→C→A) scheduling becomes impossible — no task's start can be resolved. That is why cycles have to be blocked at the moment a link is added, not at calculation time. And a cycle is usually a signal that the decomposition is wrong, so before deleting the link, re-examine the boundary between the two tasks.
- Is FS + 3-day lag different from pushing the successor start by 3 days?
- Yes. FS +3d is computed relative to the predecessor's finish, so if the predecessor slips five days the successor slips five days too. Typing a start date three days later pins that date independently of the predecessor — it stays where it is when the predecessor slips, and the critical path distorts.