Beyond Basic CI

Most teams use GitHub Actions for basic “run tests on push.” But with these tricks, you can cut CI time by 60% and automate tasks you didn’t think possible.

1. Aggressive Dependency Caching

- uses: actions/cache@v4
  with:
    path: |
      node_modules
      ~/.npm
      .next/cache
    key: deps-${{ hashFiles('package-lock.json') }}
    restore-keys: deps-

Impact: Skip npm install entirely when dependencies haven’t changed. Saves 30-90 seconds per run.

2. Matrix Builds for Parallel Testing

strategy:
  matrix:
    node: [18, 20, 22]
    os: [ubuntu-latest, macos-latest]
  fail-fast: false

Run tests across multiple Node versions and OS simultaneously. All 6 combinations run in parallel.

3. Conditional Jobs (Skip Unnecessary Work)

jobs:
  changes:
    outputs:
      backend: ${{ steps.filter.outputs.backend }}
    steps:
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            backend: 'src/api/**'

  test-backend:
    needs: changes
    if: needs.changes.outputs.backend == 'true'
    # Only runs if backend files changed

Impact: Don’t run backend tests when only docs changed. Saves minutes of CI per PR.

4. Reusable Workflows

# .github/workflows/deploy.yml
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    # ... deploy steps

Call from another workflow: uses: ./.github/workflows/deploy.yml

5. Auto-Label PRs

- uses: actions/labeler@v5
  with:
    configuration-path: .github/labeler.yml

Automatically add labels like frontend, backend, docs based on which files changed.

6. Cancel Previous Runs

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

Push again before CI finishes? Previous run cancels automatically. No wasted minutes.

7. Composite Actions for Shared Steps

# .github/actions/setup-node/action.yml
runs:
  using: composite
  steps:
    - uses: actions/setup-node@v4
      with: { node-version: 22 }
    - run: npm ci
      shell: bash
    - run: npm run build
      shell: bash

Reuse common setup steps across all workflows with one line.

Bonus: The Must-Have Action

- uses: peter-evans/create-pull-request@v6

Create PRs programmatically. Perfect for automated dependency updates, code generation, or scheduled content updates.