Skip to main content

Getting Started with CDEvents: Tracking DORA Metrics

By March 31, 2025Blog, Community, Project

From Seed to System: A Journey Through CDEvents in Practice (Part 1)

Contributed by Dadisi Sanyika, CD Foundation Governing Board Chair

CDEvents Article Image

For years, DevOps teams have sought better ways to measure and improve their software delivery performance. But here’s the problem: while teams talk about being data-driven, actually gathering accurate, enterprise-wide CI/CD metrics is incredibly difficult—especially when systems are fragmented across different tools and platforms.

When it comes to DevOps performance metrics, DORA (DevOps Research and Assessment) metrics are “top of mind” in the community.  They’ve become the gold standard for measuring software delivery performance.

At their core, DORA metrics are designed to measure software delivery performance in a way that reflects both speed and stability. They’re not just numbers—they’re indicators of how well your organization balances moving fast with maintaining reliability. In a space where teams are under constant pressure to ship quickly and safely, that balance matters.

The four key DORA metrics (Deployment Frequency, Lead Time for Change, Change Failure Rate, and Mean Time to Recover) have become widely adopted because they connect technical practice to business impact in a direct and meaningful way.

That’s why they’ve become something of a gold standard in DevOps. These metrics help teams communicate not just what they’re doing but how well they’re doing it—and where there’s room to improve.

For many organizations, DORA metrics are a natural starting point for assessing delivery maturity, aligning team efforts, and sparking conversations that lead to real, grounded change. They’re not perfect, but they are practical. Widely recognized. Actionable.

However, there’s a catch: most organizations don’t track them with real, system-generated data. Those who do tend to sift through logs or logbook entries to derive the data to use. Even the DORA research team’s annual report relies on survey-based insights from teams self-reporting their performance.

That’s also why, in this series, we start here.

Why DORA? A Conversation That Changed My Thinking

As a DevOps tool builder and someone deeply invested in solving interoperability challenges in CI/CD, I’ve spent a lot of time thinking about how to bring real data into this space. In fact, one of my key motivations for working on CDEvents is enabling teams to collect accurate, standardized metrics across disparate CI/CD systems. And naturally, when it came to demonstrating what’s possible with CDEvents, DORA seemed like the perfect place to start.

But then, something unexpected happened.

My team arranged a conversation between Nathen Harvey, who leads the DORA team at Google Cloud, and me (as Governing Board Chair of the Continuous Delivery Foundation). It was a great conversation, we dove deep, and Nathen shared very useful insights. I encourage you to check it out. During that discussion, two things became crystal clear to me:

  1. DORA research team’s annual reports are based on surveys, not an influx of hard data. But what if we could provide real, system-driven data? That’s where CDEvents comes in—it has the potential to give organizations accurate, actionable DORA metrics instead of estimates. What would that mean for the research? How would it impact our perception of our delivery processes overall?
  2. We had been thinking about “Lead Time for Change” (LTC) the wrong way. Like many tool builders, we designed CDEvents to track LTC starting at the pull request stage. But Nathen pointed out something crucial—LTC isn’t about tooling efficiency; it’s about the developer’s process and intent. We needed a way for the developer to signal “work has begun” as they intend it. That insight led me to introduce a new event into the CDEvents lexicon: branch.InitialCommit. (More about this later.)

That conversation changed the way I thought about implementing DORA support in CDEvents—and clarified where we might need to rethink our assumptions. It also reinforced why starting with DORA is the perfect first step in this blog series.

Understanding CDEvents: A Common Language for Continuous Delivery

In today’s software landscape, the tools we use are many—and they rarely speak the same language. As a result, interoperability across the CI/CD ecosystem remains one of our most persistent and complex challenges. We’ve all felt the pain: data trapped in silos, metrics that don’t line up, tools that don’t quite work together the way we need them to.

CDEvents was created to help solve that.

It’s not another tool. It’s a shared vocabulary—a standardized specification that allows the many parts of the software delivery process to communicate more clearly and consistently. Built on top of the CloudEvents specification, CDEvents is purpose-built for the continuous delivery world. It gives us a way to describe what’s happening in our systems in a format that both humans and machines can understand.

What Makes Up a CDEvents?

Each CDEvent carries two key pieces of information:

  • Context – This is the common metadata you’d expect in any well-formed event: things like the event’s ID, type, source, and timestamp. It helps systems understand when and where an event happened.
  • Subject – This describes the actual thing the event is about. For example, if the event is about a test run or a code change, the subject tells you what changed, what triggered it, and what it relates to.

And when teams need to go beyond the standard structure, there’s a customData field that allows them to include domain-specific information. That flexibility is part of what makes CDEvents adaptable across organizations and ecosystems.

How CDEvents Shows Up in the Software Lifecycle

CDEvents isn’t limited to one part of the pipeline. It’s designed to map to the full software delivery lifecycle—from the moment a developer creates a branch to the moment that code is running in production (and beyond).

Here’s how it works across the major phases:

Continuous Integration (CI)

In CI, the focus is on fast, safe integration of code changes. CDEvents provides visibility here with events like PipelineRun.Queued, PipelineRun.Started, and PipelineRun.Finished. These track the flow of changes through build and integration pipelines, giving teams a real-time view of progress and issues.

Continuous Deployment (CD)

In CD, we move from validated code to running systems. Events like ServiceDeployed and ServiceRolledback capture deployment activity and enable traceability when something goes wrong—or right. These events give teams and stakeholders insight into what’s been released, where, and when.

Testing

Testing is often where things get fuzzy in traditional pipelines. CDEvents brings clarity with structured events such as TestCase.Queued, TestCase.Started, and TestCase.Finished. These help track the full lifecycle of a test, including outcomes and durations, so teams can identify trends and troubleshoot issues earlier.

Source Code Management (SCM)

The work begins with code—and CDEvents recognizes that. Events like BranchCreated, BranchDeleted, and ChangeCreated make changes in the codebase observable. This adds much-needed context to everything that follows: builds, tests, deployments, and more.

Continuous Operations

While the core of CDEvents was born from CI/CD, it doesn’t stop there. Its flexible structure allows teams to extend it into continuous operations—capturing events around performance, incidents, or operational changes. This opens the door to a unified, end-to-end view of how systems evolve, behave, and recover over time.

A Basic Setup That Lays the Foundation

Implementing CDEvents can get complex, especially as you move toward full interoperability across sets of unlike CI/CD systems and into Data and MLOPs. That’s why we’re starting simple. In this first installment of the series, we’ll walk through a basic setup for tracking DORA metrics with CDEvents. We’ll look at how and where to emit events you can store in your favorite SQL or noSQL DB. At this stage, there’s no need for an event bus or active consumers—we’re simply starting with the basics: collecting the four key DORA metrics using CDEvents.

Here’s what we’ll do:

  • Emit CDEvents that capture the key moments needed to calculate DORA metrics.
  • Store these events, even though no consumers are acting on them yet.
  • Get accurate, system-generated DORA metrics as a first step toward more advanced implementations.

This is just the beginning. In the next post, we’ll build on this foundation by introducing event consumers that take action based on the emitted data. Eventually, we’ll progress to full interoperability using CDEvents—solving one of the biggest challenges in enterprise CI/CD.

Let’s get started!

Relating the key DORA metrics to CDEvents

Before we dive in, let’s set the right expectation—this isn’t a deep dive into the DORA research project itself. (Head over to the DORA website to learn more.) Instead, we’re going to focus on something more practical: how the key DORA metrics are calculated and how we can track them using CDEvents.

Along the way, we’ll highlight some of the nuances and challenges teams might encounter—things that aren’t always obvious at first glance. The goal is to provide a clear, hands-on approach to implementing these metrics while being mindful of the common pitfalls. 

For each key DORA metric, we will focus on a specific set of CDEvents:

DORA MetricRequired CDEvents
Deployment Frequency (DF)
How often do we deploy?
– pipelineRun.queued
– pipelineRun.finished
– service.deployed
Lead Time for Change (LTC)
How long from commit to deploy?
– branch.initialCommit
– pipelineRun.queued
– pipelineRun.finished
– service.deployed
Change Failure Rate (CFR)
What % of deployments fail?
– pipelineRun.finished
– service.deployed
– incident.identified- incident.resolved
Mean Time to Recovery (MTTR)
How quickly do we recover from failure?
– incident.identified
– incident.resolved

If you currently have a way to collect events, this approach I’m going to share should give you a starting point to begin your DORA analysis and build requirements for which tool best suits your needs. This example represents a basic implementation strategy—using simple storage like SQL and requiring no message broker—to help teams begin tracking DORA metrics with minimal setup. This starting point relies on each tool’s ability to emit events that must be converted to the CDEvents format or use a POST request to send CDEvents to your collector during the execution of the pipelines in question. This example will illustrate how to implement Deployment Frequency (DF) and Lead Time for Change (LTC).  

For this strategy example, I’ll use Github/GitLab (SCM), an Artifact repository, Jenkins (CI), and Spinnaker (CD) and assume a 1:1 mapping between Jenkins pipelines and Spinnaker applications. Additional correlation strategies are needed if you have a microservice-based architecture where multiple pipelines deploy into a single application. 

Deployment Frequency

Deployment Frequency measures how often you deploy your application to production. The CDEvents we are using to measure are:

  • PipelineRun.Queued: When a new deployment starts
  • PipelineRun.Finished: When the deployment pipeline completes and its status
  • Service.Deployed: When the deployment is complete and the service (artifact) is “healthy” in production.

The DF is the ServiceDeployed count over a given period (daily, weekly, monthly). If you are not using a dedicated workflow for your production deployment, you must filter by environment. And, of course, the PipelineRun.Finished shows the frequency of failures to help look for bottlenecks and other issues. 

If you’re using Spinnaker for the deployment, add a webhook stage at the end of the existing pipeline to send the event. The webhook stage payload with a bit of SPeL might look like this:

{
  "context": {
    "version": "0.4.1",
    "id": "${execution.id}",
    "source": "https://spinnaker.example.com/applications/${execution.application}",
    "type": "dev.cdevents.service.deployed.0.4.1",
    "timestamp": "${execution.startTime}",
    "schemaUri": "https://cdevents.dev/0.4.1/schema/service-deployed-event",
    "chainId": "${execution.trigger['parameters']['chainId']}",
    "links": []
  },
  "subject": {
    "id": "${execution.stages[-1].id}",
    "source": "https://kubernetes.example.com/namespaces/${parameters['environment']}/deployments/${execution.application}",
    "type": "service",
    "content": {
      "environment": {
        "id": "${parameters['environment']}",
        "source": "https://spinnaker.example.com/applications/${execution.application}"
      },
      "artifactId": "${trigger['artifacts'][0]['reference']}"
    }
  },
  "customData": {
    "deploymentMethod": "Spinnaker Canary",
    "deployer": "Spinnaker",
    "commitSha": "${trigger['parameters']['commitSha']}",
    "serviceName": "${execution.application}",
    "serviceVersion": "${trigger['parameters']['version']}",
    "repository": "${trigger['parameters']['repo']}"
  },
  "customDataContentType": "application/json"
}

This same HTTP POST request could be sent from Jenkins if you use the appropriate plugin. After installing it, you might do something like this:

pipeline {
    agent any
    environment {
        SERVICE_NAME = "my-app"
        DEPLOYMENT_URL = "https://kubernetes.example.com/namespaces/production/deployments/my-app"
        ENVIRONMENT = "production"
        ARTIFACT_ID = "my-app-1.0.0.jar"
    }
    stages {
        stage('Deploy') {
            steps {
                echo 'Deploying application...'
                // Simulated deployment step (e.g., Helm, kubectl, Terraform)
                sh 'sleep 5'
            }
        }
    }
    post {
        success {
            script {
                httpRequest httpMode: 'POST',
                            url: 'https://event-collector.example.com/events',
                            contentType: 'APPLICATION_JSON',
                            requestBody: """{
                                "context": {
                                    "version": "0.4.1",
                                    "id": "${env.BUILD_ID}",
                                    "source": "https://jenkins.example.com/job/${env.JOB_NAME}",
                                    "type": "dev.cdevents.service.deployed.0.4.1",
                                    "timestamp": "${env.BUILD_TIMESTAMP}",
                                    "schemaUri": "https://cdevents.dev/0.4.1/schema/service-deployed-event",
                                    "chainId": "${env.BUILD_TAG}",
                                    "links": []
                                },
                                "subject": {
                                    "id": "${env.BUILD_ID}",
                                    "source": "${env.DEPLOYMENT_URL}",
                                    "type": "service",
                                    "content": {
                                        "environment": {
                                            "id": "${env.ENVIRONMENT}",
                                            "source": "https://jenkins.example.com/job/${env.JOB_NAME}"
                                        },
                                        "artifactId": "${env.ARTIFACT_ID}"
                                    }
                                },
                                "customData": {
                                    "deploymentMethod": "Jenkins Pipeline",
                                    "deployer": "Jenkins",
                                    "serviceName": "${env.SERVICE_NAME}",
                                    "artifactId": "${env.ARTIFACT_ID}",
                                    "commitSha": "${env.GIT_COMMIT}"
                                },
                                "customDataContentType": "application/json"
                            }"""
            }
        }
    }
}

Lead Time for Change

Lead Time for Change tracks the journey from the developer’s first project commit to when that code is running in production. The CDEvents we are using to measure are:

  • branch.initialCommit: First commit in a feature branch
  • PipelineRun.Queued: When a new deployment starts
  • PipelineRun.Finished: When the deployment pipeline completes and its status
  • Service.Deployed: When the deployment is complete and the service (artifact) is “healthy” in production.

We measure how long it takes from the first commit on a branch (branch.InitialCommit)* to the deployment of that feature branch (service.deployed). Initially designed for tool-specific events, CDEvents have recently added branch.initialCommit as an experimental event to support DORA. This event is intended to function as a post-receive hook on a self-managed Git server, a webhook for vendor-managed services, or a post-commit webhook for developers working locally (basic strategy). 

The JSON schema for the branch.initialCommit has not been finalized but should land pretty close to the draft.  branch.created is currently available and widely supported, but over time, branch.initialCommit may offer clearer insight into a developer’s intent to begin meaningful work.

Change Failure Rate & Mean Time to Recovery

We can configure our incident management tools like PagerDuty to emit the CDEvent of interest using a similar approach as above: incident.identified at the start and incident.resolved. Leaving us with a bit of math to close out:

CFR = count(incident.identified linked to service.deployed) / count(service.deployed)

MTTR = avg(time(incident.resolved) - time(incident.identified))

By adopting CDEvents, your organization moves closer to true interoperability. You will unlock the ability to track, analyze, and act on software delivery data across tools, teams, and time. It’s not just about standardization—it’s about clarity, connection, and collective insight. This basic DORA example allows many teams to frame the conversation the same way.

CDEvents as a common language is how we begin to align the many voices of modern software delivery into one meaningful conversation.

What’s Next?

In the next post, we’ll explore how using an event bus can help gather key DORA metrics—especially when your service is made up of applications owned by different cross-functional teams.