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.