A Practical Guide to CI/CD Pipelines with GitHub Actions

How to design a solid CI/CD pipeline with GitHub Actions, from testing to automated deployment, with a complete working workflow file.

Why CI/CD Discipline Pays Off

Every hour spent building a reliable pipeline saves many more spent manually deploying, debugging broken releases, or chasing down “it worked on my machine” issues. GitHub Actions makes this accessible without standing up separate CI infrastructure.

A Complete Workflow Example

name: CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm test -- --coverage

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: build-output
          path: dist/
      - name: Deploy to production
        run: ./scripts/deploy.sh
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Key Design Decisions

  • Fail fast: run lint and tests before the expensive build step.
  • Cache dependencies: the cache: 'npm' option cuts install time dramatically on repeat runs.
  • Gate deploys: only deploy from main, and only after tests and build both pass.
  • Never hardcode secrets: use GitHub’s encrypted secrets store, referenced via ${{ secrets.NAME }}.

Adding Deployment Environments

GitHub Environments let you require manual approval before production deploys and scope secrets per environment (staging vs production), which prevents a staging credential leak from touching production systems.

Rollback Strategy

Tag every successful production deploy so rolling back is a matter of re-running the deploy job against a previous artifact rather than a panicked manual fix.

Conclusion

A good pipeline is boring — predictable, fast, and gated by real checks rather than trust. Start with test and build stages, add deployment gating once you have confidence in your test suite, and resist the urge to skip steps under release pressure.