caduh

An Introduction to CI/CD — what Continuous Integration & Deployment actually mean

4 min read

A simple walkthrough of Continuous Integration and Continuous Delivery/Deployment—what they solve, how a pipeline is structured, and a tiny example to get started.

TL;DR

  • CI (Continuous Integration): merge small changes frequently and run automated builds & tests on every change to catch problems early.
  • CD (Continuous Delivery): every commit is always deployable; deploying to prod is a business decision (often requires approval).
  • CD (Continuous Deployment): every green commit ships to production automatically (with safety checks).
  • Payoff: fewer integration surprises, faster lead time, safer releases, reproducible builds, and easy rollbacks.

Why CI/CD? (the problem it solves)

Before CI/CD, teams batch big changes on long‑lived branches and discover conflicts late (“integration hell”). Releases are manual, error‑prone, and stressful. CI/CD makes integration continuous and releases routine by automating: build → test → package → scan → deploy → verify.


Key concepts (speed run)

  • Pipeline: a set of stages (e.g., lint → build → test → package → scan → deploy).
  • Jobs/Runners: units of work executed on ephemeral agents.
  • Triggers: push, PR/MR, tag, schedule, or manual approvals.
  • Artifacts: build outputs (zips, Docker images) passed between stages. Build once, deploy many.
  • Caching: reuse dependencies to keep pipelines fast.
  • Environments: dev → staging → prod with the same artifact promoted through.
  • Secrets: injected at runtime (vaults, OIDC). Keep them out of the repo.
  • Observability: logs, test reports, coverage, release notes, deployment tracking.

CI vs CD vs CD (delivery vs deployment)

  • CI: run tests & checks on every change; keep main green.
  • Continuous Delivery: after CI passes, the artifact is ready for production; deploy step may be manual (approval, change window).
  • Continuous Deployment: production deploy is automatic when the pipeline is green (often with progressive delivery like canaries).

A tiny pipeline you can copy (GitHub Actions)

# .github/workflows/ci-cd.yml
name: CI/CD
on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read
  id-token: write   # for cloud/OIDC deploys

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - name: Install deps (with cache)
        run: npm ci
      - name: Lint & test
        run: npm run lint && npm test -- --ci

  build:
    needs: ci
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - name: Build
        run: npm run build
      - name: Package artifact
        run: tar -czf app.tgz dist package.json
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with: { name: webapp, path: app.tgz }

  deploy-staging:
    if: github.ref == 'refs/heads/main'
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/download-artifact@v4
        with: { name: webapp, path: . }
      - name: Deploy
        run: |
          # example: copy to server or call your deploy script
          ./scripts/deploy.sh --env=staging app.tgz

  deploy-prod:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://app.example.com
    steps:
      - uses: actions/download-artifact@v4
        with: { name: webapp, path: . }
      - name: Manual approval gate
        uses: trstringer/manual-approval@v1  # or use environment protection rules
        with: { secret: ${{ secrets.APPROVERS }} }
      - name: Deploy
        run: ./scripts/deploy.sh --env=prod app.tgz

Swap deploy.sh with your infra of choice (Kubernetes, ECS, Serverless, Railway, Fly.io). Prefer OIDC to authenticate rather than long‑lived cloud keys.


Popular tools (pick one per layer)

  • CI servers: GitHub Actions, GitLab CI, Jenkins, CircleCI, Buildkite, Azure DevOps.
  • Artifacts & registries: GitHub Packages, GHCR, ECR, GCR, Artifactory, Nexus.
  • Deploy/orchestrate: Kubernetes, Helm, Argo CD/Flux (GitOps), ECS/Fargate, Nomad, Cloud Run, App Service, Vercel/Netlify/Fly.
  • Secrets: HashiCorp Vault, AWS Secrets Manager, SSM, Doppler, 1Password, environment‑scoped secrets in your CI.

Release strategies (safe delivery)

  • Blue/Green: keep two prod stacks; switch traffic once healthy.
  • Canary: release to a small % then ramp up while monitoring.
  • Rolling: replace pods/instances gradually.
  • Feature flags: ship dark; toggle on/off without redeploy.
  • DB migrations: prefer forward‑compatible (“expand → migrate → contract”) and practice roll forward.

Test strategy (keep pipelines fast & valuable)

  • Test pyramid: many unit, fewer integration, a handful E2E.
  • Make tests deterministic; quarantine or fix flaky tests fast.
  • Parallelize and cache dependency installs/builds. Aim for < 10 minutes total CI by default.

Good practices checklist

  • [ ] Keep main always green; protect with PR checks.
  • [ ] Build once, promote the same artifact across envs.
  • [ ] Infra as Code (Terraform/Pulumi/Helm); review changes like app code.
  • [ ] Secrets from a vault/OIDC, not repo.
  • [ ] Observability on deploys (logs, metrics, traces, error rate, latency).
  • [ ] Rollback plan (and practice it).
  • [ ] Use preview environments per PR when feasible.
  • [ ] Track DORA metrics: deployment frequency, change lead time, change failure rate, MTTR.

Anti‑patterns to avoid

  • Long‑lived branches that diverge from main.
  • Building a different artifact for each environment.
  • Secrets in code or .env committed to repo.
  • Slow pipelines with no caching or unnecessary E2E tests on every push.
  • “Works on my machine” differences—use containers for build & runtime.

One‑minute adoption plan

  1. Start with lint + unit tests on PRs.
  2. Add build + artifact stage; publish to a registry.
  3. Create a staging deploy on merge to main.
  4. Add manual prod gate (Delivery) → later move to auto deploy with canaries (Deployment).
  5. Measure & tighten: flaky tests, pipeline time, rollout safety, rollback speed.