Measure an engineering team with a small set of system-level metrics: the DORA delivery metrics (deployment frequency, change lead time, recovery time, change failure rate and rework rate), a few flow measures such as review latency and CI duration, a developer experience survey, and the product outcomes the work is supposed to move. Define each metric as a formula with a named data source before anyone sees a dashboard. Use them at team level to find bottlenecks, and never to rank individuals.
Key takeaways
- Start with the DORA metrics. Three measure throughput and two measure instability, so improving one at the expense of the other shows up immediately.
- Write every metric as a formula with a data source. Most arguments about metrics are arguments about definitions.
- Use medians and multi-week trends, not averages and single weeks. Delivery data is skewed by a few outliers.
- Pair system data with a developer experience survey. The system tells you what is slow; engineers tell you why.
- Connect engineering work to product outcomes: adoption of what shipped and reliability against an SLO.
- Never use these numbers to evaluate individuals. Goodhart's law applies from the first day: when a measure becomes a target, it stops being a good measure.
Why most engineering metrics backfire
Every metric creates an incentive. Lines of code reward verbosity. Tickets closed reward splitting work into trivia. Story points inflate as soon as they are compared across teams. Commit counts reward noise. AI coding assistants have made activity metrics even less meaningful: lines and pull requests are now cheap to produce, and their count says little about whether customers got anything useful.
The metrics worth keeping share three properties. They describe the system rather than a person. They are hard to improve without actually getting better. And they come in pairs that keep each other honest, so pushing speed at the expense of quality shows up in the other number.
The DORA metrics, defined
DORA (DevOps Research and Assessment, now part of Google Cloud) has studied software delivery performance for more than a decade. Its core finding, popularized in the book Accelerate, is that speed and stability are not a trade-off: teams that deploy small changes often also tend to fail less and recover faster.
| Metric | Formula | Data source | Watch out for |
|---|---|---|---|
| Deployment frequency | count(successful production deploys) / period, per service | CD pipeline events | Re-runs and retries counted as deploys |
| Change lead time | median(deployed_at - committed_at) across the changes in each deploy | Git plus deploy log | Long-lived branches; pick commit time or PR open time and keep it |
| Change failure rate | deploys causing a failure that needed remediation / total deploys | Deploy log plus incidents, rollbacks and hotfixes | Hotfixes not linked to the deploy that caused them |
| Failed deployment recovery time | median(restored_at - impact_started_at) for deploy-caused failures | Incident tracker | Measuring until the ticket closed rather than until service was restored |
DORA now lists a fifth metric, deployment rework rate: the share of deployments that were unplanned and made because of an incident in production. It is easy to collect if every deploy records whether it is a remediation. DORA groups change lead time, deployment frequency and recovery time as throughput, and change failure rate and rework rate as instability.
Through 2024, DORA's reports grouped teams into performance clusters, and the clusters changed between reports as the survey population changed; the 2022 report found no elite cluster at all. The 2025 report took a different approach: seven team profiles that combine delivery performance with well-being. Compare against your own trend first. If you want an external reference point, read the current report at dora.dev rather than a number copied from an old blog post.
Computing the DORA metrics from your own data
Vendor tools can do this, but a small schema and four queries get you most of the value and make every definition explicit.
create table deployments (
id bigserial primary key,
service text not null,
commit_sha text not null,
finished_at timestamptz not null,
status text not null, -- 'succeeded', 'failed' or 'rolled_back'
is_remediation boolean not null default false -- a hotfix or rollback for an earlier change
);
create table deployment_changes ( -- one row per commit shipped in a deploy
deployment_id bigint references deployments (id),
commit_sha text not null,
committed_at timestamptz not null
);
create table incidents (
id bigserial primary key,
caused_by_deploy bigint references deployments (id),
impact_started_at timestamptz not null,
restored_at timestamptz
);-- Deployment frequency: successful production deploys per service per week
select service, date_trunc('week', finished_at) as week, count(*) as deploys
from deployments
where status = 'succeeded'
group by service, week
order by week;
-- Change lead time: median hours from commit to production, last 28 days
select d.service,
percentile_cont(0.5) within group (
order by extract(epoch from d.finished_at - c.committed_at) / 3600
) as median_lead_time_hours
from deployments d
join deployment_changes c on c.deployment_id = d.id
where d.status = 'succeeded' and d.finished_at >= now() - interval '28 days'
group by d.service;
-- Change failure rate: rolled back, or linked to an incident; remediations excluded
select d.service,
avg((d.status = 'rolled_back'
or exists (select 1 from incidents i where i.caused_by_deploy = d.id))::int)
as change_failure_rate
from deployments d
where d.status in ('succeeded', 'rolled_back')
and not d.is_remediation
and d.finished_at >= now() - interval '28 days'
group by d.service;
-- Recovery time: median minutes from user impact to restore, last 90 days
select percentile_cont(0.5) within group (
order by extract(epoch from restored_at - impact_started_at) / 60
) as median_recovery_minutes
from incidents
where caused_by_deploy is not null
and restored_at is not null
and impact_started_at >= now() - interval '90 days';To fill deployment_changes, have the deploy job list the commits between the previous production SHA and the new one, and post them with the deploy event:
git log --format='%H %cI' "$PREVIOUS_PROD_SHA..$GITHUB_SHA"The single most useful process change is a required "caused by deploy" field in your incident template. Without it, change failure rate is a guess. Our incident response playbook includes a template you can extend. If your pipeline runs on GitHub Actions, our guide to CI/CD with GitHub Actions shows where a deploy event step fits.
Flow metrics: where the time goes
DORA tells you how fast changes reach production. Flow metrics tell you where they wait.
| Metric | Formula | Why it matters |
|---|---|---|
| PR cycle time | median(merged_at - opened_at) | The part of lead time the team controls most directly |
| Time to first review | median(first_review_at - ready_for_review_at) | Waiting for review is often the largest single delay |
| PR size | Median lines changed per PR, generated files excluded | Large PRs wait longer, get shallower reviews and fail more |
| CI duration | p50 and p90 of pipeline time on the main branch | Slow CI makes engineers batch changes and switch context |
| Flaky test rate | test runs that failed then passed on retry with no code change / total runs | Flakiness teaches people to ignore red builds |
| Unplanned work share | time on incidents and urgent bugs / total engineering time | A rising share means the system is getting more fragile |
You can pull review latency straight from GitHub. This prints the median hours from PR creation to first review over the last 100 merged PRs:
gh pr list --state merged --limit 100 --json createdAt,reviews \
| jq '[.[] | select(.reviews | length > 0)
| ((.reviews | map(.submittedAt | fromdate) | min) - (.createdAt | fromdate)) / 3600]
| sort | .[length / 2 | floor]'Developer experience: ask the engineers
System data cannot see frustration, confusion or interruptions. Two research frameworks help structure the asking:
- SPACE (Forsgren, Storey and colleagues, 2021) describes productivity across five dimensions: satisfaction and well-being, performance, activity, communication and collaboration, and efficiency and flow. Its main lesson is that no single dimension is enough.
- DevEx (Noda, Storey, Forsgren and Greiler, 2023) narrows it to three things engineers experience directly: feedback loops, cognitive load and flow state.
A short quarterly survey is enough. Use a five-point agreement scale and keep the questions concrete:
- I can get a change from my laptop to production without waiting on other people.
- CI gives me a result I trust, quickly enough that I do not switch to something else.
- I can understand the code I need to change without asking its author.
- I had uninterrupted focus time on most days last week.
- My dev environment worked the first time I needed it this month.
- Being on call last month was sustainable.
- Open question: what slowed you down most in the last month?
The open question produces the most useful data. Publish what you learned and what you will change, or response rates will drop by the second survey.
Product outcomes: did the work matter?
Delivery metrics can look excellent while the team ships things nobody uses. Add a few outcome measures that connect engineering work to customers.
Feature adoption. For each significant release: accounts that used the feature within 30 days of release / active accounts that could use it. Track it for every launch in the quarter, not just the successful ones. Our growth engineering guide covers the event tracking this depends on.
SLO attainment and error budget. For each tier-1 service, define a service level objective and compute the error budget as (1 - SLO) x window. A 99.9% availability objective over 30 days allows 43.2 minutes of unavailability. When the budget is spent, reliability work takes priority over features until it recovers.
Cost per unit. Cloud spend per customer, per thousand requests or per transaction. It catches architecture decisions that work functionally and fail economically.
A starter dashboard
| Metric | Definition | Source | Review cadence | Owner |
|---|---|---|---|---|
| Deployment frequency | Successful deploys per service per week | CD events | Weekly | Team leads |
| Change lead time | Median commit to production | Git and CD | Weekly | Team leads |
| Change failure rate | As defined above, 28-day window | CD and incidents | Every two weeks | Team leads |
| Recovery time | Median impact to restore, 90-day window | Incident tracker | Monthly | Engineering manager |
| Time to first review | Median, last 100 PRs | GitHub | Weekly | Team leads |
| CI duration | p90 on main | CI provider | Weekly | Platform owner |
| Developer experience | Survey scores and themes | Survey | Quarterly | Engineering managers |
| SLO attainment | Per tier-1 service | Monitoring | Weekly | Service owners |
| Feature adoption | Per launch, 30 days after release | Product analytics | Monthly | Product and engineering |
Start with the top four and time to first review. Add the rest when someone has a question the first five cannot answer.
Turning metrics into changes
A metric earns its place only when it leads to a decision. Use a simple loop:
- Notice a trend, over several weeks, not a single bad week.
- Investigate with the team. Ask what is behind the number before proposing anything. The answer may be CI capacity, unclear requirements, oversized PRs or a team that is short-staffed.
- Write a hypothesis, such as "if we cap PR size and add a review rotation, time to first review drops".
- Change one thing for four to six weeks.
- Compare, including the paired metric. Faster reviews with a rising change failure rate is not a win.
- Keep or revert, and write down which.
As a team grows, the same metrics show where structure is starting to strain. Our guide to scaling an engineering team covers what typically breaks at each size.
What goes wrong with engineering metrics in practice
Deploy counts inflated by re-runs. A flaky pipeline re-run three times counts as three deploys. Count unique successful deploys per service and commit.
Lead time skewed by a few branches. One branch that lived for two months drags the average for the quarter. Use the median, and look at the slowest tenth separately to find the real outliers.
Change failure rate undercounted. Hotfixes ship without a link to the deploy that caused the problem, so failures never get attributed. Treat rollbacks as failures automatically and require the "caused by" field.
Recovery time measured to ticket closure. The incident was resolved in twenty minutes, but the ticket was closed on Monday. Measure until service was restored for users.
Monorepos hide per-service behavior. One pipeline deploys five services and the metrics average them together. Attribute deploys to services.
Gaming. Teams split PRs into trivial pieces to improve cycle time or ship empty changes to raise deploy counts. When a number improves and nothing else does, look at how it was achieved.
Leaderboards. Once individual numbers are visible and compared, people stop reviewing each other's code and stop taking on hard problems, because both make their own numbers worse.
Surveys nobody acts on. Engineers answer once, see nothing change, and stop answering. Close the loop publicly.
Frequently Asked Questions
What are the four DORA metrics?
The original four DORA metrics are deployment frequency, change lead time, change failure rate and failed deployment recovery time. DORA now adds a fifth, deployment rework rate: the share of deployments that are unplanned and made because of an incident in production. DORA groups lead time, deployment frequency and recovery time as throughput, and change failure rate and rework rate as instability.
How do you calculate change failure rate?
Divide the number of production deployments that caused a failure needing remediation, such as a rollback, a hotfix or an incident, by the total number of production deployments in the same period. Exclude the remediation deployments themselves from the count. The calculation is only as accurate as the link between incidents and the deployments that caused them.
Should you measure individual developer productivity?
No. Individual metrics such as commits, pull requests or lines of code are easy to game and push people away from reviewing code, mentoring and tackling hard problems. Measure delivery and developer experience at team level, and evaluate individuals through their manager's judgment of impact, quality and collaboration.
What is the difference between DORA and SPACE?
DORA is a set of five delivery metrics that measure how quickly and reliably a team ships changes to production. SPACE is a framework describing developer productivity across five dimensions: satisfaction and well-being, performance, activity, communication and collaboration, and efficiency and flow. Most teams use DORA for system data and SPACE to decide what to ask in developer surveys.
What is the difference between lead time and cycle time?
Change lead time, in DORA's definition, runs from code being committed to that code running in production. Cycle time usually refers to a narrower part of the process, such as the time from opening a pull request to merging it, or from starting work on a ticket to finishing it. Define both explicitly on your dashboard, because teams use the terms differently.
How do you measure developer experience?
Run a short quarterly survey with concrete questions about feedback loops, cognitive load and focus time, using a five-point agreement scale plus one open question about what slowed people down. Combine the results with system data such as CI duration and review latency. Publish what you learned and what you will change, or engineers will stop responding.
Need help measuring your engineering team?
We are senior engineers who run what we build: if we built it, we are on the pager, so we care about the numbers that predict a bad night. We can instrument your pipeline and incident process for delivery metrics, find the bottlenecks behind them, and fix the ones that are engineering problems.