Self-Service CI/CD for AWS

Self-service AWS CodePipeline platform — developers ship compliant CI/CD pipelines in minutes via dashboard, CLI, CDK, or AI prompt, while platform teams enforce policy-as-code guardrails, governance, and per-team isolation.

DORA Metrics

Overview

This document explains Pipeline Builder’s DORA metrics — the four DevOps Research and Assessment delivery-performance indicators — covering how each is defined, the performance-level bands, the deploy tag standard that produces them, and the endpoints. It’s for platform teams and engineering leaders tracking delivery health. DORA is an advanced analytics feature gated behind the advanced_reporting entitlement (included on Enterprise, or the Advanced Reporting add-on on other tiers) and the reports:read permission.

These metrics are DEPLOY-BASIS ONLY. Every metric derives from real deploy-stage executions — there is no run-based mode. A pipeline that only builds/tests (no deploy stage) produces no DORA data. This is a deliberate, no-backward-compatibility change: the old run-based frequency, the median-run-duration lead-time proxy, and the inferred CFR/MTTR are removed.

The panel is empty until pipelines re-synth. Deploy attribution comes from tags that pipeline-core writes at synth time. Already-deployed pipelines emit no DORA data until they re-synth with the new deploy tags and run again — the panel starts empty and fills forward. That is expected, not a regression. Historical pre-cutover data is excluded; there is no migration or backfill.

Process overview

  1. Declare — a user sets an environment on each deploy stage. pipeline-core derives the pb.deploys tag (see Declaring deployments).
  2. Ingest — the events Lambda parses pb.deploys, sets environment on the deploy-stage events, resolves the source commit range in-account (oldest unshipped commit time + count) — only when enabled with setup-events --with-dora (the commit enrichment is an add-on cost; see below) — and forwards normalized events to the reporting service.

Enabling lead time (--with-dora). Commit-timestamp resolution makes SCM calls and reads the org’s github-token secret in your AWS account, so it’s off by default and gated on the Lambda’s DORA_ENABLED env var. Turn it on with pipeline-manager infra setup-events --with-dora (only worthwhile for orgs holding the advanced_reporting add-on; re-run to toggle after a later purchase). With it off, standard reporting still works and DORA lead time reports unknown.

  1. Compute — DORA is derived over the deploy-stage executions in the window: deployment frequency, two-class change-failure rate, measured lead time, production MTTR, and coverage.
  2. Classify — each metric gets a level band (elite/high/medium/low, or null when there’s no sample).
  3. Surface — results render as per-environment Reports-page cards (headline production), or are consumed via the endpoints.

Declaring deployments

A deployment is a pipeline stage that ships to an environment. You declare it by setting an environment on the deploy stage; pipeline-core emits two CodePipeline tags the forwarder reads:

Tag Value
pb.pipeline-id the platform pipelineId (the registry join key)
pb.deploys <stage>:<env> pairs joined by +, e.g. Deploy-stg:staging+Deploy-prod:production

How each metric is defined

All metrics are computed per environment over the deploy-stage executions in the window (deploy completed_at range). Cross-source time deltas are clamped ≥0.

Performance levels

Each metric carries a level band (elite / high / medium / low, or null when there’s no sample). Thresholds follow the DORA/Accelerate reports:

Metric Elite High Medium Low
Deployment Frequency ≥ 1/day ≥ 1/week ≥ 1/month slower
Change Failure Rate ≤ 5% ≤ 10% ≤ 15% > 15%
Mean Time To Restore < 1 hour < 1 day < 1 week ≥ 1 week
Lead Time < 1 day < 1 week < 1 month ≥ 1 month

The dashboard renders each band as a colored badge; null bands show no badge.


Endpoints

DORA metrics

GET /api/reports/execution/dora?from=<iso>&to=<iso>&includeDescendants=<bool>

Requires the reports:read permission and the advanced_reporting feature.

Param Values Default Notes
from ISO 8601 timestamp 30 days ago Start of the window
to ISO 8601 timestamp now End of the window
includeDescendants true, false false Roll the aggregate over the org → team subtree. Requires reports:rollup; ignored otherwise.
pipelineId pipeline id Restrict to a single pipeline (per-pipeline DORA).
environment environment name Restrict to a single deploy environment.

The window is capped at the org’s effective DORA retention (min(730, doraRetentionDays), absolute ceiling 730 days) — a wider range returns HTTP 400. See Retention.

DORA trend

GET /api/reports/execution/dora/trend?interval=<day|week|month>&from=<iso>&to=<iso>

Returns data.trend — deploy frequency + deploy-time change-failure rate, bucketed by interval on the deploy completed_at. Same guards, rollup, and optional scoping (pipelineId/environment) as /dora. Each point:

{ "period": "2026-07-01T00:00:00.000Z", "deployments": 4, "failed": 1, "total": 5, "changeFailurePct": 20 }

Post-deploy outcomes

POST /api/reports/deployments/:executionId/outcome

Body { "outcome": "failed" | "restored", "at": "<iso>", "environment": "<name>?" }. Marks a deployment failed (a production incident linked to the deploy) or restored. Feeds the post-deploy CFR component and real MTTR. advanced_reporting-gated, org-scoped, and idempotent — re-posting the same (execution, outcome) refreshes at instead of double-counting.

Ingest health

POST /api/reports/ingest-health

Body { "forwarded": <int>, "dropped": <int>, "lastEventAt": "<iso>" }. Posted by the AWS events Lambda (machine reporting:ingest scope; org taken from the token identity) so the Reports UI can show flowing / stale / dropping. One row per org.

Prometheus metrics

On ingest, the reporting service increments (exposed on its /metrics, scraped by in-cluster Prometheus):

result is succeeded failed.

Incidents (automated post-deploy failures)

POST /api/reports/incidents

Body { "incidentId", "environment", "openedAt", "resolvedAt"?, "severity" }. Posted by your incident tooling (PagerDuty / Datadog / Alertmanager) using the machine reporting:ingest scope — the same credential the event forwarder holds; the org is taken from the token identity. Idempotent on (org, incidentId) — a later resolve re-post updates resolvedAt. Each incident is correlated to the most recent successful deploy to its environment with completed_at ≤ openedAt within DORA_INCIDENT_WINDOW_HOURS (default 24, overridable per-org), producing an automated post-deploy CFR signal + a real MTTR.

Companion routes (all advanced_reporting-gated):

Configured self-serve from Settings → Incident Reporting (org-admin). See Incident webhook for the full contract, payload, provider setup, token issuance, and the admin UI.

Build health

GET /api/reports/execution/build-health?pipelineId=<id>&from=<iso>&to=<iso>

Per-pipeline build health — per-stage run counts, success rate, and duration percentiles (p50Ms/p90Ms/p99Ms) rolled up per stage from the pipeline’s STAGE events. Requires only reports:read — it is standard reporting, available on every tier (NOT advanced_reporting-gated). pipelineId is required. Returns data.buildHealth = { stages: [{ stage, runs, successes, failures, successRate, p50Ms, p90Ms, p99Ms }], totals: { runs, failures, failureRate } } (totals sum across stages). Rendered on the Reports page as a Build Health sub-panel next to the DORA panel, keyed by the scoped pipeline.


Retention

Reporting rows do not live forever — a leader-locked background sweep in the reporting service hard-deletes expired rows by created_at on a split schedule, so high-volume standard events expire faster than the low-volume DORA source. Both windows are per-org overridable; unset falls back to a global env default.

Window Covers Default Env default Per-org override
Standard events pipeline_events with environment IS NULL (non-deploy STAGE/ACTION/build activity) 30 days REPORTING_EVENT_RETENTION_DAYS dora_settings.event_retention_days
DORA source pipeline_events with environment IS NOT NULL (deploy stages) + all deployment_outcomes + all incidents 180 days REPORTING_DORA_RETENTION_DAYS dora_settings.dora_retention_days

Response

data.dora has the following shape:

{
  "data": {
    "dora": {
      "window": { "from": "2026-06-27T00:00:00.000Z", "to": "2026-07-27T00:00:00.000Z" },
      "filters": { "pipelineId": null, "environment": null },
      "headline": "production",
      "environments": [
        {
          "environment": "production",
          "deploymentFrequency": { "deployments": 128, "perDay": 4.27, "level": "elite" },
          "changeFailureRate": { "rate": 7.9, "deployTimeFailures": 8, "postDeployFailures": 3, "attempts": 139, "level": "high" },
          "leadTime": { "deployments": 120, "medianSeconds": 5400, "level": "high" }
        }
      ],
      "meanTimeToRestore": { "incidents": 4, "restored": 3, "medianSeconds": 1840, "level": "high" },
      "coverage": { "registered": 20, "deploying": 12, "withoutDeploys": 8 }
    }
  }
}
Field Meaning
window.from / window.to The resolved reporting window (deploy completed_at range)
filters.pipelineId / filters.environment The scoping applied (echoed), or null
headline The headline environment name (production)
environments[] Per-environment cards (headline first, then A→Z)
environments[].deploymentFrequency deployments (successful deploys), perDay, level
environments[].changeFailureRate Two-class: rate, deployTimeFailures, postDeployFailures, attempts, level
environments[].leadTime deployments (median sample), medianSeconds (null = unknown), level
meanTimeToRestore Production-only: incidents, restored, medianSeconds (null when none), level
coverage registered, deploying, withoutDeploys
*.level Performance band: elite/high/medium/low, or null