CI/CD

Building Reliable CI/CD Pipelines with GitHub Actions

Build once, use short-lived identities, protect release gates, and verify the artifact you actually deploy.

Delivery is a chain of trust

A useful pipeline connects a reviewed change to a verified running service. A green job alone does not establish that the correct artifact reached the correct environment. Reliability depends on reproducible builds, trustworthy inputs, least-privilege permissions, clear release gates, and explicit verification.

The examples here describe a hypothetical service. They are educational fragments, not a complete production workflow or evidence of any personal deployment outcome.

Separate untrusted validation from privileged delivery

Pull-request checks can run formatting, unit tests, and dependency evaluation without production credentials. Delivery should be triggered from a protected branch or an approved environment using a workflow whose changes are themselves reviewed. Be particularly careful with events that execute pull-request code in a privileged context.

Set permissions explicitly. Use read-only defaults, then grant write access only to the job that needs it. OIDC can replace stored cloud access keys with short-lived credentials, but the cloud trust policy must restrict the repository, branch or environment, and expected audience. OIDC is not secure merely because it is enabled.

YAML · EDUCATIONAL EXAMPLE
name: Validate
on:
  pull_request:
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: npm
      - run: npm ci
      - run: npm test

For compactness, this example uses action major-version tags. In a hardened workflow, pin third-party actions to reviewed full commit SHAs and update them through a controlled process. Choose a supported Node version compatible with your project, and ensure a lock file is committed for the package manager cache.

Build once and promote the same image

Build an OCI image from the reviewed commit, generate useful provenance, scan dependencies and image contents, and publish the image to a registry. Record the image digest. Promote that digest through staging and production rather than rebuilding the same source separately for each environment.

Rebuilding can change dependencies, build arguments, or base images. A mutable tag such as latest also breaks the connection between the artifact that passed tests and the artifact that is deployed. Use environment configuration separately from the immutable application image.

GateWhat it establishesWhat it does not prove
Unit testsExpected isolated behaviorReal dependency availability
Integration testsSelected boundaries work togetherEvery production failure mode
Image scanKnown findings in scanned contentAbsence of vulnerabilities
Smoke checksBasic target-environment behaviorLong-term reliability

Keep feedback fast without weakening gates

Run independent checks in parallel, cache only safe reproducible inputs, and keep the critical path short. A dependency cache should speed installation without replacing lock-file validation. Never cache secrets or let an untrusted cache become the source of executable code in a privileged job.

Flaky tests deserve explicit ownership. Re-running until green hides a signal rather than fixing it. Separate deterministic release gates from exploratory tests, and track failures that are quarantined so they are not forgotten.

Deployment requires a concurrency policy

Multiple releases to the same environment can overwrite each other or make rollback confusing. Use environment-scoped concurrency and decide whether an in-progress deployment may be cancelled. Cancellation is reasonable for disposable preview environments; interrupting a database migration or a production apply can leave partial state.

Approvals should protect meaningful risk boundaries, not simply add waiting time. Protect environment secrets and restrict the identities allowed to approve. Database changes need backward-compatible sequencing when old and new application versions run simultaneously.

Verify behavior after the release

Wait for application readiness, run smoke checks through the real entry point, and inspect error and latency indicators over a stated verification window. A Kubernetes rollout reporting success is necessary but not sufficient: it shows readiness conditions, not that every business workflow works.

Maintain a known-good revision and a documented rollback or roll-forward strategy. A database schema change may make application rollback unsafe, so compatibility must be planned before deployment. Connect release observations to the next planning cycle and assign owners to improvements. That closes the lifecycle instead of ending it at a green button.

CI/CD supply-chain security covers OIDC trust policy, image provenance, and promotion gates. Zero-downtime Kubernetes deployments explains what the deploy job should wait for. See DevOps & CI/CD and the CI/CD lab.

Related technical guides

LET’S BUILD WHAT’S NEXT

Good engineering starts with a conversation.

Let’s connect