What it is
Two common ways to measure schedule position:
- Earned value: compare the budgeted value of work actually done (earned value, EV) with the budgeted value of work planned by now (planned value, PV).
- Milestones: days between the baseline date and the forecast or actual date for key milestones.
Some owners and public contracts require earned value reporting. Many contractors track milestone days in their scheduling tool.
Why operators track it
- Late jobs cost money. Extended general conditions, overtime, and possible liquidated damages under the contract.
- Revenue timing. Slower progress means less earned revenue this period and a longer backlog.
- Crew planning. A slipping job may need more crews, or release them late to the next job.
- One language. Earned value puts schedule and cost in the same dollars.
How it is calculated
Schedule variance = earned value − planned value
- Planned value (PV) = budgeted cost of work scheduled to date, from the baseline schedule.
- Earned value (EV) = physical percent complete × budget at completion.
- SPI = EV ÷ PV. Below 1.0 is behind schedule.
- For milestones: days late = forecast date − baseline date.
Worked example
Job 2420 planned $290,000 of budgeted work by October 31. Physical progress is 31% of a $718,000 budget, so earned value is $222,580. Schedule variance is −$67,420 and SPI is 0.77.
How to read it
- Use physical progress for EV. Cost-to-cost percent complete measures spending, not progress, and makes SV meaningless.
- SV dollars go to zero at the end. When all work is done EV equals PV, so late in a job milestone days are more informative.
- Check the baseline. A re-baselined schedule hides past slip. Keep the original.
- We do not publish a benchmark.
Definitional choices and common pitfalls
- Dollars or days. Earned value gives dollars; milestone tracking gives days. Many reports show both.
- Progress method. Units installed, weighted milestones, or PM estimate. Not cost to date.
- Baseline. Original or current approved baseline. Say which.
- Critical path only. For days late, measure critical path milestones, not every activity.
How different roles use it
| Role | How they use it |
|---|---|
| Project manager | Tracks progress against the baseline and recovers slip. |
| Operations / superintendent | Updates field progress and look-ahead schedules. |
| Owner / CEO | Sees which jobs are slipping and by how much. |
| CFO / controller | Considers schedule when reviewing forecasts and backlog timing. |
Questions it answers
- Which jobs are behind plan?
- How much budgeted work is late?
- Which milestones are slipping?
- Will the job finish on the contract date?
How to present it in a report
- KPIs for SPI and days late on the next milestone.
- PV and EV lines over time per job.
- A milestone table with baseline, forecast, and days late.
| Milestone | Baseline | Forecast | Days late |
|---|---|---|---|
| Slab on grade complete | Sep 15 | Oct 20 | 35 |
| Foundations complete | Aug 29 | Sep 12 | 14 |
| Steel erection start | Nov 3 | Dec 8 | 35 |
Data you need
Fields
| Field | What it means | Example value |
|---|---|---|
| Baseline schedule | Planned activities with dates and budget loading | Slab on grade complete, Sep 15 |
| Physical percent complete | Progress by activity or cost code | 31% |
| Budget at completion | Revised budget | $718,000 |
| Milestone forecast date | Current forecast | Oct 20 |
| Data date | As-of date for the update | Oct 31 |
What the raw data looks like
Example data A few rows as they might come out of the source system.
| Job | Milestone | Baseline | Forecast | Days late | Planned value to date | Physical % |
|---|---|---|---|---|---|---|
| 2420 | Foundations complete | Aug 29 | Sep 12 | 14 | ||
| 2420 | Slab on grade complete | Sep 15 | Oct 20 | 35 | ||
| 2420 | Job total (Oct 31) | $290,000 | 31% |
Where this data usually lives
- Scheduling tools hold the baseline and current schedule, activities, and milestones.
- Field reporting (daily logs, foreman reports) holds quantities installed and site events.
- Project management often holds change orders, commitments, and project documents. For example: Procore.
- Construction ERP and accounting holds job cost, budgets, billing, AP, and AR. For example: Sage 300 Construction and Real Estate, Viewpoint Vista (Trimble), and Foundation. Some smaller contractors use general accounting software such as QuickBooks with jobs set up as projects.
The same field can live in more than one system. Agree with the customer which one is the source of truth.
Common data gaps and traps
- Schedules not cost-loaded. Without cost loading, PV must be estimated from a planned percent curve.
- Scheduling tool separate. Schedules often live in a dedicated tool or files; link activities to cost codes.
- No physical progress. Many systems only have cost percent complete.
- Re-baselined schedules. The original baseline may be overwritten.
Related metrics
- Labor productivity: slow crews cause slip.
- Backlog: slip pushes revenue out.
- Cost to complete: extended duration adds cost.
FAQ
- Why not use cost-to-cost for earned value?
- Cost to date measures spending. A job that overspends would look ahead of schedule.
- What is SPI?
- Schedule performance index, EV divided by PV. 1.0 is on plan.
- Is earned value required?
- Some owners and public contracts require it. Many contractors use milestones only.
For your data teamFormula variants, tables, SQL sketch, and dashboard spec
Formula details
pv = SUM(budgeted cost of activities scheduled by :data_date)
ev = physical_pct_complete * budget_at_completion
sv = ev - pv
spi = ev / pv
days_late = forecast_date - baseline_date -- per milestone
Worked example detail
| Item | Job 2420 |
|---|---|
| Planned value | $290,000 |
| Physical % complete | 31% |
| Budget at completion | $718,000 |
| Earned value | $222,580 |
| SV | −$67,420 |
| SPI | 0.77 |
Tables and fields
| Table | Fields | Notes |
|---|---|---|
schedule_activities |
job_id, activity_id, baseline_start, baseline_finish, budget_amount, cost_code | |
progress |
job_id, activity_id, data_date, physical_pct | |
milestones |
job_id, name, baseline_date, forecast_date, actual_date |
SQL sketch
SELECT a.job_id,
SUM(a.budget_amount * LEAST(1, GREATEST(0, (:data_date - a.baseline_start)::float / NULLIF(a.baseline_finish - a.baseline_start,0)))) AS pv,
SUM(a.budget_amount * p.physical_pct) AS ev
FROM schedule_activities a JOIN progress p USING (job_id, activity_id)
WHERE p.data_date = :data_date GROUP BY a.job_id; -- assumes linear loading within each activity
Build spec
Copy this into a ticket. It lists the grain, inputs, logic, and edge cases.
metric: construction.schedule_variance
grain: one row per job per data date; one row per milestone
inputs:
- baseline activities with budget
- physical percent complete
- milestone dates
logic:
- pv from baseline loading up to data date
- ev = physical % * BAC
- sv = ev - pv; spi = ev / pv
- days_late = forecast - baseline
edge_cases:
- pv = 0 (job not started): spi null
- no physical progress: do not substitute cost %
- re-baseline: keep original and current
As a dashboard card
- Line of PV and EV per job, KPI for SPI, and a milestone table sorted by days late.
Common mistakes
- Using cost-to-cost percent complete as EV.
- Overwriting the baseline.
- Reporting SV dollars late in the job without days.