Measuring delivery
The four DORA numbers every team is judged on.
Part 1: The problem
QuickCart has started releasing every week instead of every month. At the planning meeting, the founder asks a simple question: “Are we getting better?”
Leo says yes: they ship four times as often. Noor says no: last Tuesday’s release broke checkout. Theo says the on-call nights feel worse. Everyone is describing something real, and nobody can answer the question.
QuickCart doesn’t need opinions. It needs those four numbers.
Part 2: The idea
The DORA metrics are four numbers that together describe how well a team delivers software. Two measure speed. Two measure stability.
| Metric | The question it answers | How to measure it | Measures |
|---|---|---|---|
| Deployment frequency | How often do we put changes in production? | Count deployments to production in a period | Speed |
| Lead time for changes | How long does a change take from commit to production? | For each change: time deployed minus time of its first commit | Speed |
| Change failure rate | What share of our deployments hurt users? | Deployments that needed a fix, rollback or hotfix, divided by all deployments | Stability |
| Time to restore | When a deployment hurts users, how long until it’s fixed? | For each failure: time fixed minus time it started | Stability |
Why all four, and never just one
Any one number on its own can be made to look good in a harmful way:
| If you only watch | You can “improve” it by | Which hurts |
|---|---|---|
| Deployment frequency | Deploying the same big batch more often, or skipping tests | Change failure rate |
| Change failure rate | Deploying rarely and testing for weeks | Deployment frequency and lead time |
| Lead time | Rushing changes through | Change failure rate |
| Time to restore | Marking incidents fixed before they really are | Users, who are still affected |
Watched together, the only reliable way to improve all four is the one the previous lesson showed: smaller batches, with automated testing and deployment. A small change is quick to ship, rarely breaks things, and is easy to undo when it does.
What good looks like
Past DORA reports sorted teams into groups. The boundaries change from year to year, so treat these as rough:
| Metric | The strongest teams | The weakest teams |
|---|---|---|
| Deployment frequency | Several times a day | Less than once a month |
| Lead time for changes | Under a day | More than a month |
| Change failure rate | Around 5 to 15% | Around half of deployments |
| Time to restore | Under an hour | More than a week |
The point isn’t to hit a group’s numbers. It’s to know where you are, and to see whether a change you make, like adding automated tests, moves all four the right way.
Two rules for using them
- They describe a team’s system, not a person. Rank people by them and people will game them: split one change into ten tiny deploys, and frequency looks wonderful while nothing gets better.
- Measure from what your tools already record. Your git history, your deploy logs and your incident records hold most of it. The lab shows how far plain git gets you.
Part 3: See it
Change how many changes go out in each release, and watch all four numbers. The squares are the changes in one release; red ones are bad changes hiding among the rest.
A longer bar is better. A simple model, not real data: about 4 changes finished a day, each with a 3% chance of causing a problem.
What to notice: all four numbers get worse together as the batch grows. There’s no setting where you trade a little speed for a lot of stability. Big batches make you slower and more fragile.
Part 4: Do it
Free — you only read your own project’s history.
You’ll measure deployment frequency, lead time, change failure rate and time to restore for a real project, using only git. About 20 minutes.
You need a git project that deploys from its main branch, where each merge to main goes to production. Most web and backend projects work like this. If your project’s main branch is called something else, such as master, use that name wherever you see main. Open the terminal in the project’s folder.
The example output below comes from QuickCart’s order service. Yours will show your own history.
List what reached main in the last 30 days
Terminalgit log --first-parent --since="30 days ago" --format="%h %ad %s" --date=iso mainPart What it means git loglist commits, newest first --first-parentonly the commits made on main, such as merges, not every commit inside each branch that was merged--since="30 days ago"only the last 30 days --format="%h %ad %s"each line shows the short ID, the date and the message --date=isodates with the time of day, like 2026-09-16 10:45:00 +0000mainthe branch to read You should seec14440c 2026-09-22 15:30:00 +0000 Merge pull request #45 from eta dc69072 2026-09-16 10:45:00 +0000 Merge pull request #44 from promo2 9c91e1d 2026-09-09 18:02:00 +0000 Merge pull request #43 from fix-total 6bb6f9d 2026-09-09 16:20:00 +0000 Merge pull request #42 from promo 15374d9 2026-09-03 11:05:00 +0000 Merge pull request #41 from menu-photosEach line is one change reaching
main, so, in a project that deploys every merge, one deployment. Keep this list open; the later steps use its times.Count them: deployment frequency
Terminalgit rev-list --count --first-parent --since="30 days ago" mainPart What it means git rev-listlist commit IDs, with no other detail --countprint only how many there are --first-parent --since="30 days ago"the same commits as the last step mainthe branch to count You should see5QuickCart deployed 5 times in 30 days: a little more than once a week. That’s its deployment frequency.
Measure one change: lead time
Pick a merge from your list. Put its ID in place of the highlighted one in both places, and list the commits it brought in:
Terminalgit log --reverse --format="%h %ad %s" --date=iso "dc69072^1..dc69072^2"Part What it means git log --reverselist commits, oldest first --format="%h %ad %s" --date=isoID, date with time, and message dc69072^1the merge’s first parent: what mainlooked like just before the mergedc69072^2its second parent: the tip of the branch that was merged in A..Bcommits reachable from B but not from A. Here: exactly the commits the branch added the quotes stop the terminal treating ^as a special characterYou should seee1159a2 2026-09-14 09:00:00 +0000 Promo codes at checkout, with tests for totals dea5847 2026-09-15 14:10:00 +0000 Handle expired codesThe first commit was on 14 September at 09:00. From step 1, the merge was on 16 September at 10:45. So this change’s lead time was about 2 days and 2 hours, plus however long your deploy takes after a merge. Do the same for three or four merges and take the typical value.
Find the failures: change failure rate
Look for changes that undid or patched an earlier one:
Terminalgit log --since="30 days ago" --format="%h %ad %s" --date=iso -i --grep="revert" --grep="hotfix" mainPart What it means git log --since="30 days ago"commits from the last 30 days. No --first-parentthis time, so commits inside merged branches are included--format="%h %ad %s" --date=isoID, date with time, and message -iignore upper and lower case --grep="revert" --grep="hotfix"messages containing either word mainthe branch to read You should seea8bbe85 2026-09-09 17:05:00 +0000 Revert promo codes: totals wrong for some ordersOne of QuickCart’s 5 deployments, the promo codes in #42, had to be undone. Its change failure rate is 1 ÷ 5 = 20%.
Git only knows about failures someone fixed with a commit. If your team keeps incident records, count from those instead; they’re more complete.
Time the recovery: time to restore
For each failure, find when the bad change went out and when the fix went out, both in the step 1 list:
Event From the list Time The bad change is deployed 6bb6f9d Merge pull request #42 from promo16:20 The fix is deployed 9c91e1d Merge pull request #43 from fix-total18:02 QuickCart’s time to restore was 1 hour 42 minutes. Strictly, the clock starts when users were first affected, which your monitoring knows better than git. The deploy time is a good first estimate.
Write down your four numbers
Metric QuickCart Yours Deployment frequency 5 in 30 days Lead time for changes About 2 days Change failure rate 20% Time to restore About 2 hours You should seeFour numbers for your project. Look at the one that’s furthest from the strongest teams in the idea. Measure again in a month to see if it moved.
Clean-up step: Clean up
Nothing to delete. Every command here only reads git history.
Part 5: Check yourself
Which two DORA metrics measure speed?
A team starts deploying ten times more often, and their change failure rate goes up. What should they conclude?
What does lead time for changes measure?
QuickCart made 20 deployments last month. 3 of them caused problems that needed a fix. What is the change failure rate?
A manager wants to rank every developer by their personal deployment frequency. What's the problem?
Part 6: Cheat sheet
| Metric or command | In one line |
|---|---|
| Deployment frequency | How often you deploy to production. Speed |
| Lead time for changes | Commit to running in production. Speed |
| Change failure rate | Share of deployments that needed a fix. Stability |
| Time to restore | From a failure starting to it being fixed. Stability |
| The rule | Watch all four together. Smaller batches improve all of them |
| Never | Use them to rank individual people |
git rev-list --count --first-parent --since="30 days ago" main | How many changes reached main |
git log --reverse "ID^1..ID^2" | The commits a merge brought in, oldest first |
git log -i --grep="revert" | Changes that undid earlier ones |