Cloud & DevOps

Cloud DevOps Advice From 2024 That Now Fails in Production

A green pipeline is a weak definition of fast delivery when the same pipeline can ship an unverified image, miss a five-minute error spike, or leave a database change that cannot be undone. Over the last two years, the production risk has shifted from making deployments repeatable to proving what ran and detecting harm quickly. I would optimize for time to a safe decision, not deployment frequency alone.

Green CI no longer proves that the release is trustworthy

The repeatable-build advice in Cloud DevOps Best Practices for Faster Software Delivery is still useful, but I would not treat a passing build as release approval because it says little about the artifact deployed or the behavior observed afterward. A workflow can test one commit and deploy an image built from another; a mutable tag such as latest makes that mistake hard to reconstruct during an incident. Record the source commit, image digest, workflow run, and deployment revision together, then make the promotion job use the tested digest rather than rebuilding the image.

This distinction matters more now because hosted CI is part of the production trust boundary. GitHub made artifact attestations generally available in 2024, giving teams a practical way to bind build provenance to an artifact. The 2025 compromise of tj-actions/changed-files demonstrated a different failure mode: a third-party workflow action can expose secrets before your application tests even begin. Neither development changes the purpose of CI; both make “the tests passed” an incomplete answer to “what did we release?”

For GitHub Actions, I would pin third-party actions to full commit SHAs, set the workflow’s permissions as narrowly as each job permits, and use GitHub OIDC to request short-lived cloud credentials instead of storing a long-lived deployment key. Require an attestation for the image digest at promotion and verify it against the expected repository and workflow identity. SLSA provenance and Sigstore cosign offer useful formats and verification tooling, but an attestation proves an origin claim, not that the code is safe. Keep vulnerability scanning and tests, while refusing to let either substitute for provenance.

I would not make an OpenSSF Scorecard result a hard production gate because a repository-level score cannot establish that a particular deployed digest passed the controls you expect. I would use it to find maintenance gaps, then gate promotion on checks tied to the actual artifact. That is more work than adding a green badge, but it gives the on-call engineer an answer they can verify during a rollback.

Automated promotion is outdated when it cannot reject a bad canary

Deploying on every merge can reduce batch size, but automatic promotion is unsafe when success means only that Kubernetes reported available replicas. A readiness probe checks whether a container should receive traffic; it does not tell you whether checkout requests now fail, a queue backs up, or p95 latency rises. Kubernetes rollout status is therefore a deployment-state check, not a user-impact check.

For an HTTP service, a release gate should query the same signals operators use during an incident: a Prometheus request counter, a latency histogram, and a saturation signal relevant to the service. OpenTelemetry can carry telemetry through the application and Collector, but verify the resulting metric names and labels before copying a query into CI; http_requests_total below is an example instrument name, not a universal convention. A dashboard that looks correct while its release query returns no series is a particularly quiet failure.

The following Bash gate runs against a reachable Prometheus API with curl, jq, and awk installed. It fails if the selected service has no request series, and fails when its five-minute HTTP 5xx fraction exceeds the example threshold. Set PROM_URL and SERVICE to values from your environment before running it.

#!/usr/bin/env bash
set -euo pipefail
: "${PROM_URL:?set PROM_URL}"
: "${SERVICE:?set SERVICE}"
query="((sum(rate(http_requests_total{service=\"${SERVICE}\",status=~\"5..\"}[5m])) or vector(0)) / sum(rate(http_requests_total{service=\"${SERVICE}\"}[5m])))"
value=$(curl -fsSG --data-urlencode "query=$query" "$PROM_URL/api/v1/query" |
  jq -er '.data.result[0].value[1] | tonumber')
awk -v v="$value" 'BEGIN {exit !(v <= 0.01)}'

The 1% error threshold is a value to tune against the service’s SLO, not a recommendation to copy; an endpoint with a much tighter error budget needs a lower limit. The five-minute window is an illustrative observation period, not proof that a low-traffic canary has gathered enough evidence. Require a minimum request count, check latency separately, and account for scrape delay before allowing this gate to promote anything. Otherwise a quiet canary can pass because it has not encountered the failing path.

There is also a real tooling choice. Plain Kubernetes Deployment plus a scripted gate wins when traffic routing is simple and the team can maintain a small, auditable promotion script; it costs operator time to coordinate the gate with rollout and rollback. Argo Rollouts wins when a service needs staged traffic shifts and metric-driven analysis across several steps; it costs another controller, custom resources, routing integration, and failure modes to rehearse. I would not install Argo Rollouts merely to automate a check that nobody has validated against a real production incident.

Rollback advice is wrong when application state has moved forward

“Deploy often and roll back quickly” remains attractive advice, but changing an image digest does not reverse a database write, a published event, or a new field consumed by another service. The checklist in Cloud DevOps Best Practices for Faster Software Delivery can help standardize delivery steps, but I would add an explicit compatibility test because a successful redeploy does not prove that yesterday’s binary understands today’s data.

Use an expand-and-contract migration when an old and new application version may run together. Add a nullable column or compatible field first; deploy code that can read both representations; backfill with observable, restartable jobs; switch writers only after readers are ready; and remove the old field in a later release. PostgreSQL migrations deserve particular scrutiny because DDL locking and a long backfill can outlast the application rollout. For example, a job that updates rows in bounded batches gives operators a pause point that one large transaction does not. Test the previous application version against the expanded schema before declaring rollback viable.

Events need the same discipline. A consumer that rejects an unknown JSON field, or a producer that changes an Apache Kafka message’s meaning under the same schema, can make an image rollback ineffective. Where teams use a schema registry, set an appropriate compatibility policy and test representative old consumers against new messages. Where they do not, keep versioned fixtures in CI and replay them before promotion. Neither approach rescues a destructive change that has already overwritten the only copy of needed data, which is why a restore drill remains distinct from a deployment rollback drill.

Give the on-call engineer a rollback limit rather than a vague promise. As an initial exercise, run the previous image for 30 minutes against a staging environment with the expanded schema and realistic queued events; that duration is a rehearsal setting to adjust for your workload, not a safety guarantee. Record the last point at which reverting only the image is safe, the command that performs that revert, and who can stop a backfill. If a release crosses that point, its response plan is forward repair or data recovery, not an automatic kubectl rollout undo.

The fastest production pipeline is the one operators can interrupt

Start with the next release, not a platform redesign. Pick one service and trace its commit through GitHub Actions, its OCI image digest, its Kubernetes revision, and its Prometheus series. Then deliberately make a staging canary fail and confirm that promotion stops before traffic expands. If the team cannot identify the running digest or state when image-only rollback becomes unsafe, fix those two gaps before shortening the deployment interval.