CI/CD Pipeline Best Practices with GitHub Actions

A practical guide to building robust CI/CD pipelines with GitHub Actions -- covering workflow design, caching, secrets management, matrix builds, and deployment strategies.

AA

Abiyyu Abidiffatir Al Majid

3 min read
CI/CD Pipeline Best Practices with GitHub Actions

GitHub Actions has become one of the most popular CI/CD platforms thanks to its tight integration with GitHub, generous free tier, and a massive marketplace of reusable actions. But a poorly configured pipeline can be slow, flaky, and hard to maintain. Here is how to build one that is fast and reliable.

Workflow Structure

A well-organized workflow separates concerns into distinct jobs. Here is a typical Node.js CI pipeline:

# .github/workflows/ci.yml
name: CI

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

concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true

jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - run: pnpm install --frozen-lockfile - run: pnpm lint

test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - run: pnpm install --frozen-lockfile - run: pnpm test -- --coverage - uses: actions/upload-artifact@v4 with: name: coverage path: coverage/

build: needs: [lint, test] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - run: pnpm install --frozen-lockfile - run: pnpm build

Notice the concurrency block -- it cancels in-progress runs when a new commit is pushed to the same branch, saving CI minutes.

Caching Dependencies

Caching is the single biggest performance win. The setup-node action supports caching for npm, yarn, and pnpm out of the box:

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'pnpm'  # automatically caches ~/.pnpm-store

For more granular control, use actions/cache:

- uses: actions/cache@v4
  with:
    path: |
      ~/.pnpm-store
      node_modules/.cache
    key: deps-${{ runner.os }}-${{ hashFiles('/pnpm-lock.yaml') }}
    restore-keys: |
      deps-${{ runner.os }}-

Secrets Management

Never hardcode secrets. Use GitHub encrypted secrets and pass them as environment variables:

env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}
  API_KEY: ${{ secrets.API_KEY }}

steps: - run: pnpm test env: # Only expose secrets to steps that need them TEST_DB_URL: ${{ secrets.TEST_DATABASE_URL }}

Best practices:

  • Use Environment-scoped secrets** for production credentials (requires approval gates).
  • Rotate secrets regularly.
  • Use GITHUB_TOKEN for GitHub API operations -- it is automatically provided and scoped to the repository.

Matrix Builds

Test across multiple versions or platforms in parallel:

test:
  runs-on: ubuntu-latest
  strategy:
    matrix:
      node-version: [18, 20, 22]
      database: [postgres, mysql]
    fail-fast: false  # don't cancel other jobs if one fails
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'pnpm'
    - run: pnpm install --frozen-lockfile
    - run: pnpm test
      env:
        DB_TYPE: ${{ matrix.database }}

Deployment Strategies

For deployments, use GitHub Environments to add approval gates and protection rules:

deploy-production:
  needs: [build]
  runs-on: ubuntu-latest
  environment:
    name: production
    url: https://example.com
  steps:
    - uses: actions/checkout@v4
    - name: Deploy to Vercel
      uses: amondnet/vercel-action@v25
      with:
        vercel-token: ${{ secrets.VERCEL_TOKEN }}
        vercel-args: '--prod'

Key Takeaways

  • Use concurrency to cancel redundant runs and save CI minutes.
  • Always cache dependencies -- it can cut install times from minutes to seconds.
  • Scope secrets tightly: prefer environment-scoped secrets for production credentials.
  • Use matrix for cross-platform/cross-version testing, but keep the matrix reasonable to avoid combinatorial explosion.
  • Leverage GitHub Environments for deployment approval gates.
#CI/CD#GitHub Actions#DevOps#Automation
AA

Crafted by

Abiyyu Abidiffatir Al Majid

Software Engineer passionate about building scalable web applications and sharing knowledge about modern web development, system design, and emerging technologies.

Related Articles