Skip to content
ExponaBLOG
PROMETHEUS, GRAFANA & MONITORING · DAY 088 / 100

DORA metrics: what every Monitoring developer should know

Measure delivery performance.

Expona Admin7 min read

You've invested months in deploying microservices, containerizing your stack, and building a solid observability layer. But how do you actually know if your team is shipping faster and more reliably? DORA metrics: what every Monitoring developer should know gives you the framework to answer that question with data rather than gut feeling. These four measurements—deployment frequency, lead time for changes, change failure rate, and time to restore service—form the backbone of modern delivery performance assessment.

DORA (DevOps Research and Assessment) metrics emerged from years of research by Dr. Nicole Forsgren and colleagues studying high-performing teams. Unlike vanity metrics, DORA focuses on outcomes that genuinely matter: how often you deploy, how long changes take to reach production, how often they break things, and how quickly you fix those breaks. For monitoring engineers and platform teams, understanding these metrics isn't optional—it's foundational to building systems that support healthy delivery cadences.

Deployment frequency

Deployment frequency measures how often your team successfully releases code to production. This isn't about pushing to a staging branch; we're counting actual production deployments. The metric typically ranges from daily to monthly for most organizations, with elite performers deploying multiple times per day.

Why does frequency matter? More frequent deployments mean smaller changes, faster feedback loops, and lower blast radius when something goes wrong. A team deploying once every six months carries far more risk per release than one deploying daily. The psychological effect also matters: developers and operators both gain confidence when deployments are routine events rather than rare, stressful occasions.

To track this in your monitoring stack, you need deployment events flowing into Prometheus or your metrics store. Most teams capture this via webhooks from their CI/CD system—GitLab CI, GitHub Actions, or your internal orchestrator. Here's a typical pattern:

yaml
groups:
  - name: deployments
    interval: 30s
    rules:
      - alert: DeploymentFrequencyCheck
        expr: |
          increase(deployments_total[1d]) > 0
        for: 5m
        labels:
          severity: info
        annotations:
          summary: "Production deployment detected in the last 24h"
          environment: "{{ $labels.environment }}"

A practical example: your Kubernetes cluster runs a webhook receiver that increments a counter each time a successful deploy completes. By the end of each sprint, you query that counter to see how many times you hit production. If you're seeing 2-3 per day, that's healthy for most teams. If it's 0-1 per month, you've got a pipeline bottleneck worth investigating. The metrics team can then correlate deployment frequency with other signals—outage rates, incident response time, team morale—to build the business case for process improvements.

Lead time for changes

Lead time measures the elapsed time from when code is committed to when it successfully runs in production. Unlike cycle time (which includes idle time in queues), lead time captures the total duration from developer push to live traffic hitting your change. For high performers, this often ranges from minutes to hours; for lower performers, days or weeks.

Long lead times create a dangerous feedback loop. Developers forget context by the time their code ships. Bugs accumulate without quick corrections. Teams lose confidence in their own changes. Conversely, short lead times mean developers see the real-world impact of their work almost immediately.

Measuring lead time requires integration between your version control and deployment systems. Most modern CI/CD tools can inject timestamps and commit hashes into your monitoring system. Here's how you might structure this:

yaml
# Pseudo-code for a metrics collector
def on_deployment_success(commit_hash, deploy_time):
    commit_timestamp = git_log(commit_hash).timestamp
    lead_time_seconds = deploy_time - commit_timestamp
    lead_time_histogram.observe(
        lead_time_seconds,
        labels={
            "service": service_name,
            "environment": "production"
        }
    )

Once you're collecting this data, build dashboards that show the distribution—median lead time, 95th percentile, and outliers. A real-world scenario: you notice API service deployments consistently take 6+ hours while worker service deployments take 15 minutes. Investigation reveals the API service runs extensive integration tests that the worker skips. The team debates: do we add those tests to the worker, or are they overkill? Data-driven conversation beats opinions.

Change failure rate

Change failure rate (CFR) is the percentage of deployments that result in degradation requiring immediate remediation—rollback, hotfix, or incident response. This filters out false alarms; a "failure" is something that genuinely harmed users or stability, not a minor warning in logs.

High CFR indicates inadequate testing, poor code review discipline, or insufficient staging environment fidelity. It's correlated with longer lead times (big changes accumulate more bugs) and lower deployment frequency (teams become gun-shy about deploying after failures). Healthy CFR typically sits between 5-15% for most organizations; elite teams often run at 5% or lower.

Measuring CFR requires correlating deployments with incidents. Your incident management system (PagerDuty, Opsgenie, or custom) should emit events to your observability layer when on-call engineers get paged for production issues. Then you correlate incident timestamps with deployment history:

yaml
# Calculate CFR as a time-series metric
def on_incident_resolution(severity, deploy_hashes_since_incident_start):
    if severity in ["critical", "high"] and deploy_hashes_since_incident_start:
        # One or more deployments preceded this incident
        for hash in deploy_hashes_since_incident_start:
            failed_deployment_counter.inc(
                labels={"commit": hash, "service": service_name}
            )

A practical scenario: your team's CFR has been steady at 8%, but this week it jumped to 25%. Investigation shows three rapid deployments to the payment service, two of which introduced bugs. The team decides to extend their contract tests and add a mandatory peer review gate for payment-related changes. Next week, CFR drops to 6%. You've just used metrics to drive process improvement.

Time to restore service

Time to restore service (TTRS) measures how long it takes from incident detection (or user report) to full resolution. Unlike mean time to recovery (MTTR), which some define as time from alert to "issue acknowledged," TTRS is the full end-to-end duration. It captures your team's capability to detect problems, diagnose root cause, apply fixes, and verify resolution.

TTRS reflects operational maturity. Fast TTRS indicates strong runbooks, experienced on-call staff, good monitoring coverage, and rapid deployment pipelines. Slow TTRS suggests missing dashboards, unclear alerting, understaffing, or deployment delays.

Capture TTRS by recording incident lifecycle events in your observability system:

yaml
# Incident timeline tracking
def log_incident_event(event_type, incident_id, timestamp):
    if event_type == "created":
        incident_start_time.labels(incident_id=incident_id).set(timestamp)
    elif event_type == "resolved":
        start = incident_start_time.labels(incident_id=incident_id).get()
        ttrs_seconds = timestamp - start
        ttrs_histogram.observe(
            ttrs_seconds,
            labels={"severity": incident.severity, "service": incident.service}
        )

Real example: your database service experiences a connection pool exhaustion incident. Monitoring detects elevated latency at 14:32, but the on-call engineer doesn't get paged until 14:47 (missing alert routing). Investigation takes until 15:10, fix deploys at 15:22, and full stability returns by 15:35. Total TTRS: 63 minutes. Broken down: 15 minutes to alerting, 23 minutes to diagnosis, 12 minutes to fix, 13 minutes to verification. Your next focus: fix the alert routing (quick win) and pre-stage connection pool documentation in runbooks (prevents future re-diagnosis).

Common mistakes

Mistake 1: Confusing deployment frequency with code commit frequency. Commits are not deployments. You might merge 50 PRs per day but only deploy twice per week due to manual approval gates. Track what actually reaches production, not what lands in main.

Mistake 2: Setting DORA targets without context. "We want 100 deployments per day" doesn't make sense if you're operating three services. Per-team or per-service benchmarks are more meaningful. Also, the goal isn't maximum frequency; it's frequency that your team can sustain without burning out.

Mistake 3: Ignoring outliers in lead time calculations. One deployment per sprint that takes weeks due to organizational delays will skew your median. Separate "normal path" lead time from "special case" lead time to see your typical experience.

Mistake 4: Counting all incidents as deployment failures. If your service goes down due to network misconfiguration or cloud provider issues, that's not a change failure rate issue. CFR should reflect quality of deployments, not external factors. Use labels to distinguish root causes.

Mistake 5: Not accounting for deployment size in CFR. A massive deployment affecting five services will naturally have higher failure risk than a single-function change. Normalize CFR by deployment scope or track it separately per service.

Key takeaways

  • DORA metrics translate operational maturity into four concrete numbers: how often you deploy, how long changes take, how often they fail, and how fast you fix them.
  • Deployment frequency and lead time push toward smaller, faster releases; change failure rate and time to restore service measure the quality and resilience of those releases.
  • Collect these metrics via CI/CD webhooks, incident management system integrations, and git event streams—they require observability infrastructure but no external SaaS.
  • Use DORA trends to identify process bottlenecks (long lead times, low deployment frequency) and quality gaps (high change failure rate) rather than as targets to chase blindly.
  • Correlate DORA metrics with team satisfaction surveys and business outcomes to build the case for investment in tooling, testing, and process improvements.
  • Review DORA metrics monthly, not daily. Real trends emerge over weeks; daily noise obscures signal.

DORA metrics aren't just for compliance or board meetings. They're a lens through which you can understand whether your monitoring, testing, and deployment infrastructure is actually serving your team's delivery goals. If you're seeing consistently poor metrics in one dimension—say, long lead times despite frequent commits—you've found where your next infrastructure investment belongs.

ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles