Every repo I own runs the same four workflows. They took a week of iteration to land on and they've been unchanged for a year since. This is the setup, with the reasoning behind each piece.
The four jobs worth automating
Most CI templates do too much. After trimming, the ones that consistently earn their keep are:
- Lint + typecheck on every PR — catches the cheap mistakes before a human reads the diff.
- Preview deploys for every branch — reviewers click a link instead of pulling the branch.
- Weekly dependency updates — batched into one PR, not one per package.
- Nightly housekeeping — stale branch cleanup, old artifact purge, cache warmup.
Anything beyond these tends to be noise until the team is large enough to need it.
Lint and typecheck job
name: CI
on:
pull_request:
jobs:
check:
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 run typecheckTwo details worth calling out. First, cache: npm on the setup-node action is enough — you don't need a separate actions/cache step for node_modules. Second, npm ci is non-negotiable over npm install in CI: it respects the lockfile exactly and fails loud when it drifts.
Preview deploys without platform lock-in
If you're on Vercel or Netlify, previews come free. Everywhere else, a minimal Action plus any static host works. The pattern is the same: build on PR, upload the output, post a comment with the URL.
Weekly dependency batching
Dependabot's default behavior — one PR per package per week — is unusable on any repo with more than five dependencies. The fix is the grouped update config, which batches minor and patch updates into a single PR. Keep major updates separate so they get proper review.
What I don't automate
Changelog generation, release tagging, and version bumps stay manual. They happen rarely enough that the cognitive cost of a broken release script exceeds the saved minutes. Automate the boring frequent stuff. Leave the rare stuff to humans.