DELIVERY GUIDE · UPDATED 11 AUG 2026

CI/CD: shipping without ceremony

I introduced CI/CD to my teams in 2017 when releases were quarterly events with war rooms. Today charger-facing services deploy multiple times a week, mid-day, without anyone holding their breath. The tooling was the easy part.

The pipeline as the only path

One rule made everything else work: nothing reaches production except through the pipeline. No SSH hotfixes, no manually copied DLLs, no exceptions for emergencies — especially not for emergencies. When the pipeline is the only path, every deploy is reproducible, auditable, and revertible; the 2 a.m. fix goes through the same tests as the Tuesday feature.

What every service’s pipeline runs

  • Build & unit tests: fast feedback under 5 minutes — slower than that and developers stop waiting for it.
  • Static analysis & dependency scanning: vulnerabilities blocked at PR time cost minutes; found in production they cost audits.
  • Contract tests: OCPI/OCPP payload compatibility verified against recorded partner interactions — the protocol seam is where regressions hide.
  • Deploy to staging, smoke, promote: the same image, environment config injected — what was tested is what ships.

Deployment strategies by risk

  • Stateless APIs: rolling replacement with instant rollback — deploys are non-events.
  • Billing and CDR services: blue-green with a manual gate and a verification window — the money path earns extra ceremony.
  • Charger-connection services: drain-and-shift — existing OCPP WebSocket connections migrate gracefully, never dropped mid-session.
  • Database migrations: expand-and-contract, always backward compatible one version — schema and code deploy independently or rollback is a lie.

The cultural part nobody budgets for

CI/CD fails as a tooling project and succeeds as a habit change. Small PRs had to become normal, broken builds had to become everyone’s immediate problem, and “who approved this deploy” had to become “the pipeline did, here’s the evidence.” That took a year of consistent leadership; the YAML took a week.

Worked example: expand-and-contract migration

The discipline that makes rollback real: schema and code deploy independently, each version compatible one step back:

-- Deploy N: EXPAND - add nullable column, backfill async
ALTER TABLE charging_session ADD stop_reason VARCHAR(32) NULL;
-- code version N writes both old and new columns

-- Deploy N+1: code reads new column, falls back to old
-- (rollback to N is safe: old column still written)

-- Deploy N+2: CONTRACT - only after N+1 is stable everywhere
ALTER TABLE charging_session DROP COLUMN legacy_stop_flag;

Three small deploys instead of one risky one — at no point does a rollback strand the schema ahead of the code.

FAQ

How often should a platform team deploy?

As often as changes are ready — for my teams that is several times a week per service. Frequency is a health indicator: when deploys are scary, teams batch; when they batch, risk compounds.

What blocks a release in your pipelines?

Failed tests, failed contract checks, high-severity CVEs, and missing approvals on gated environments. Everything else warns but never blocks — a pipeline that cries wolf gets bypassed.

Trunk-based or GitFlow?

Short-lived branches merged to main within a day or two, feature flags for incomplete work. Long-lived branches are merge debt with interest.