Skip to main content
DevOps · lesson 1 of 8
DevOps beginner Lesson 1 of 8

What DevOps Actually Is, Measured From a Real Repo

DevOps isn't a tool or a job title — it's practices that show up as a measurable number. Compute deployment frequency from two real git histories and the difference between them is the whole definition.

By SkillByExample · Published

devops dora-metrics git deployment-frequency

Ask ten people to define DevOps and you’ll get ten answers. Compute one number from two real git histories and the difference explains itself.

A slow-releasing team’s real history

git for-each-ref --sort=creatordate --format="%(creatordate:short) %(refname:short)" refs/tags
2026-07-01 v0.1.0
2026-07-15 v0.2.0
2026-07-29 v0.3.0
2026-08-12 v0.4.0

Four real tags, each created by an actual commit at an actual point in time — not a description of a release cadence, the release cadence itself, as it actually happened.

Measuring deployment frequency

# deploy_frequency.py
import subprocess
from datetime import date

output = subprocess.run(
    ["git", "for-each-ref", "--sort=creatordate", "--format=%(creatordate:short) %(refname:short)", "refs/tags"],
    capture_output=True, text=True, check=True,
).stdout.strip()

dates = []
for line in output.splitlines():
    date_str, tag = line.split(maxsplit=1)
    y, m, d = map(int, date_str.split("-"))
    dates.append((date(y, m, d), tag))

print(f"Releases found: {len(dates)}")
for d, tag in dates:
    print(f"  {d}  {tag}")

gaps = [(dates[i][0] - dates[i - 1][0]).days for i in range(1, len(dates))]
avg_gap = sum(gaps) / len(gaps)
print(f"\nGaps between releases (days): {gaps}")
print(f"Average days between deploys: {avg_gap:.1f}")
print(f"Deploys per week: {7 / avg_gap:.2f}")
python deploy_frequency.py
Releases found: 4
  2026-07-01  v0.1.0
  2026-07-15  v0.2.0
  2026-07-29  v0.3.0
  2026-08-12  v0.4.0

Gaps between releases (days): [14, 14, 14]
Average days between deploys: 14.0
Deploys per week: 0.50

Half a deploy per week — a release every two weeks, like clockwork. Not good or bad on its own yet; it’s a baseline to compare against.

The same script, a different real repo

git for-each-ref --sort=creatordate --format="%(creatordate:short) %(refname:short)" refs/tags
2026-08-01 v1
2026-08-02 v2
2026-08-03 v3
2026-08-04 v4
2026-08-04 v5
2026-08-05 v6
python deploy_frequency.py
Releases found: 6
  2026-08-01  v1
  2026-08-02  v2
  2026-08-03  v3
  2026-08-04  v4
  2026-08-04  v5
  2026-08-05  v6

Gaps between releases (days): [1, 1, 1, 0, 1]
Average days between deploys: 0.8
Deploys per week: 8.75

Same script, unmodified, run against a different repo’s real tag history: 8.75 deploys per week versus 0.50 — a 17x difference, including one day (Aug 4) with two separate releases. That gap is what “DevOps maturity” actually looks like as a number, not an adjective. Nothing about job titles changed between these two repos; what changed is how small a unit of work gets shipped, and how much automation exists to ship it safely at that frequency.

Practice

1. Compute the total time span covered by the slow team's releases.
first, last = dates[0][0], dates[-1][0]
print(f"Span: {(last - first).days} days across {len(dates)} releases")
Span: 42 days across 4 releases

42 days, 4 releases — confirms the same thing the average gap already showed, from a different angle: three intervals of exactly 14 days each, spanning six weeks total.

2. Handle a repo with only one tag, where no gap can be computed.
if len(dates) < 2:
    print("Not enough releases to measure a gap yet.")
else:
    # existing gap calculation
    pass
Not enough releases to measure a gap yet.

A real edge case worth guarding: zip-style gap calculations over a list of length 1 (or 0) either error or silently produce an empty result — checking len(dates) < 2 first gives a clear message instead of either failure mode.

3. Compute deploys per week for the fast team using the total span instead of averaging gaps.
first, last = dates[0][0], dates[-1][0]
span_days = (last - first).days
print(f"Alternate calc: {len(dates) - 1} intervals over {span_days} days = {7 * (len(dates) - 1) / span_days:.2f} deploys/week")
Alternate calc: 5 intervals over 4 days = 8.75 deploys/week

Same 8.75 result via a different formula (total intervals over total span, rather than averaging individual gaps) — a useful sanity check that the average-of-gaps method isn’t hiding a calculation mistake.

Next: a decision framework for choosing a CI/CD toolchain, the same weighted-scoring approach cloud/choosing-a-cloud.md used for picking a cloud provider.

Frequently Asked Questions

Isn't DevOps just a job title for someone who does both dev and ops?
That's a common misreading, but the term describes a set of practices — continuous integration, automated deployment, fast feedback loops, shared ownership of production — not a role. Two teams with identical job titles can have wildly different DevOps maturity, which is exactly what this lesson's two repos show: same kind of work, ten times the deployment frequency.
Why measure deployment frequency from git tags instead of just asking the team how often they release?
Self-reported cadence is often optimistic and rarely precise — 'we release pretty often' could mean daily or monthly depending on who's asked. Git tags (or any consistent release marker) are an actual historical record: every date is real, and the gaps between them are arithmetic, not opinion.
Is more frequent deployment always better?
It correlates strongly with other DevOps outcomes (shorter lead time, lower change failure rate) in the DORA research this track's later lesson covers, but frequency alone isn't the goal — it's a symptom of small, well-tested, automatically-deployable changes. A team forcing frequent deploys without the automation and testing to support them just ships more incidents faster.
What counts as a 'deploy' for this measurement?
Whatever your team's actual release marker is — a git tag, a GitHub release, a deployment record from your CD tool. This lesson uses tags because they're universal and need no external service, but the same script logic applies to any dated list of release events, including ones pulled from a real deployment API.
Why does this track exist separately from the terraform/docker/kubernetes tracks already on this site?
Those tracks each teach one tool in depth. This track is about the practices and decisions that sit above any single tool — which CI/CD approach fits a team, how to choose between deployment strategies, how to measure whether any of it is actually working. It cross-references those tracks rather than repeating their syntax.