The competency people skip because it sounds like management material, and then it costs them, because it is eminently testable: "write a query showing deployment frequency" is a concrete task. The trick is knowing which existing metrics stand in for which indicator.
make core obsOrientation
"Measure platform efficiency" in practice means plumbing the delivery tool's metrics into the monitoring stack and querying them. The wiring is the exercise; the PromQL is five lines.
Delivery performance (the DORA four) says how well software reaches production. Platform effectiveness (fulfillment latency, adoption, time to first contribution, satisfaction) says how well the platform serves its users. A platform can ship fast and still be miserable to use, and vice versa, which is exactly why the white paper lists both.
The indicators worth naming
| Indicator | Definition | Data lives in |
|---|---|---|
| Deployment frequency | how often you release to production | the CD tool (sync counters), or git tags |
| Lead time for changes | commit → running in production | git + CD: commit timestamp vs sync timestamp |
| Change failure rate | share of releases causing degradation | rollout outcomes, failed syncs, incident links |
| Time to restore service | incident start → recovered | alerting/incident data (firing → resolved) |
| Fulfillment latency | request → capability delivered | your own CR timestamps (creation → Ready) |
| Adoption / time to first contribution | who uses the platform, how fast a newcomer ships | outside the cluster: catalog data and surveys |
In an Argo CD shop, the controller's own metrics are the delivery data
argocd_app_sync_total: counter per app with aphaselabel (Succeeded, Failed, Error). Deployment frequency and change failure rate come straight out of this.argocd_app_info: one series per app carrying currentsync_statusandhealth_statusas labels. Anything about "how many apps are unhealthy right now" is a count over this.argocd_app_reconcile(histogram): controller loop duration; a platform-health signal rather than a delivery one.
Argo CD exposes them, but this Prometheus does not scrape them until someone says so. That someone is you, below.
The other half: does the platform run what it shipped?
Workload restart rates (kube_pod_container_status_restarts_total), pods not at desired replicas (kube_deployment_status_replicas_unavailable), pending pods, and the request/usage gap from section 1.5. Deployment metrics tell you the platform ships fast; these tell you it runs what it shipped. A good answer to "how would you measure this platform" names both halves.
Deployment frequency counted from syncs is an approximation: a self-healing sync of an unchanged app is not a deployment, and one push that updates ten apps is not ten deployments. Say the approximation you are making and what would make it exact (counting distinct revisions, or emitting deployment events from the pipeline). Naming a metric's limitation is what separates measurement from theatre.
Exercises
Discover what Argo CD exposes, then monitor it:
kubectl -n argocd get svc | grep metricsThree metrics services (application controller, server, repo server). The lab's gitops layer enables them; if the grep comes back empty on an older install, that is your first finding, and helm -n argocd upgrade argocd argo/argo-cd --reuse-values --set controller.metrics.enabled=true --set server.metrics.enabled=true --set repoServer.metrics.enabled=true is the fix, itself a fair exam-shaped task. Write one ServiceMonitor per service you care about; the controller's is the one with sync metrics:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: argocd-controller
namespace: monitoring
labels: { release: prometheus }
spec:
namespaceSelector: { matchNames: [argocd] }
selector: { matchLabels: { app.kubernetes.io/name: argocd-metrics } }
endpoints: [{ port: http-metrics }]That endpoint port is http-metrics because that is what the Service names it, not because anyone would guess it: kubectl -n argocd get svc argocd-application-controller-metrics -o jsonpath='{.spec.ports[*].name}' is the thirty-second check that beats an hour of "why is there no target".
argocd_app_info returns rows in the Prometheus UI within a couple of minutes. Everything from 4.1 about selectors and port names applies; this exercise is deliberately a rerun of those skills against an unfamiliar target.With sync activity from your 2.2/2.6 work in the counters (trigger a couple of argocd app sync demo-staging runs if the range is empty):
sum(increase(argocd_app_sync_total{phase="Succeeded"}[24h]))
sum(increase(argocd_app_sync_total{phase=~"Failed|Error"}[24h]))
/ sum(increase(argocd_app_sync_total[24h]))
count(argocd_app_info{health_status!="Healthy"}) or vector(0)Deployment frequency, change failure rate, and currently-degraded apps (a time-to-restore ingredient). Then push one bad image tag (the 2.6 drill), sync, revert, and watch the failure rate query move.
argocd app history demo-staging. A metric you have personally made move is a metric you understand.Measure the white paper's "request to fulfillment" for the lab's own self-service path: apply a fresh AppEnvironment XR (section 3.5) and time from apply to Ready condition using its lastTransitionTime:
kubectl get appenvironment <name> -o jsonpath='{.status.conditions[?(@.type=="Ready")].lastTransitionTime}'against the object's metadata.creationTimestamp.
The bridge to 4.2: a PrometheusRule that fires when any app stays non-Healthy for 10 minutes (count(argocd_app_info{health_status!="Healthy"}) > 0, for: 10m). Verify with the bad-image trick, then clean up.
Self-check
Name the DORA four and the system that holds each one's raw data in this lab.
Deployment frequency: Argo CD sync counters. Lead time: git commit timestamps joined with sync timestamps. Change failure rate: sync phases plus rollout outcomes. Time to restore: alerting data (firing to resolved), or incident records. None of them live in one place, which is itself the point.
Why increase() rather than the raw counter for deployment frequency?
The raw counter is cumulative since the controller started and resets on restart. increase(...[24h]) gives the count within the window and handles resets. For a daily figure that is the right shape; rate would give you a per-second number nobody wants to read.
Your new ServiceMonitor for Argo CD produces no target. First two checks?
The endpoint's port against the Service's actual port name (http-metrics, not metrics, not 8082), and the label selector against the Service's real labels. Then, as always, Prometheus's own serviceMonitorSelector and namespace selector.
How would you measure "request to fulfillment" for a self-service API?
Difference between the CR's metadata.creationTimestamp and the lastTransitionTime of its Ready condition. Aggregate it by exporting that delta as a metric (a small exporter or a recording rule over kube-state-metrics custom resource state), then track its p50/p95 over time.
Someone proposes measuring developer productivity by lines of code. Reply in one sentence.
Measure outcomes, not output: DORA's four keys plus platform-effectiveness measures (fulfillment latency, adoption, satisfaction) describe whether the system delivers value, whereas line counts reward volume and are trivially gamed.
Docs to know your way around
- argo-cd.readthedocs.io: the metrics page (metric names and labels).
- dora.dev: definitions, one page each; the exam-usable summaries.
- tag-app-delivery.cncf.io: the white paper's measurement section, for the platform-specific measures.
- Offline: the Prometheus UI's metric explorer against the
argocd_prefix, which is faster than any documentation.