Automation with CI/CD

Advanced · 19 min read · ▶ live playground · ✦ checkpoint

Code review gives a team human judgment. Automation gives the team repeatable checks. CI/CD is the habit of running those checks and deployments from the same branch and pull request workflow you already learned, so every change gets the same basic safety pass before it merges or ships.

CI is the shared "run the checks" machine

CI means continuous integration: automated work that runs when code is pushed or a pull request opens. For a JavaScript project, the first CI job usually runs the same scripts people already run locally:

{
  "scripts": {
    "lint": "eslint .",
    "typecheck": "tsc --noEmit",
    "test": "vitest run",
    "build": "vite build"
  }
}

You met those pieces earlier:

  • Section 19 put linters, formatters, npm scripts, and build tools behind project commands.
  • Section 20 made typecheck an explicit TypeScript command.
  • Section 21 made test a repeatable behavior check.
  • Section 23 made pull requests the place where review and checks meet.

CI does not replace any of those ideas. It connects them. If a teammate forgets to run npm test locally, CI still runs it. If a reviewer misses a formatting or type error, CI still reports it. The point is not to make humans unnecessary; it is to stop wasting human attention on checks a machine can run exactly the same way every time.

A first GitHub Actions workflow

GitHub Actions workflows live in .github/workflows/. A first checks workflow can be small:

name: Checks
 
on:
  pull_request:
  push:
    branches: [main]
 
jobs:
  checks:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
 
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
 
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test

Read it top to bottom:

  • on says when the workflow runs: for pull requests, and for pushes to main.
  • jobs.checks names one job.
  • runs-on chooses a Linux runner.
  • checkout downloads the repository into the runner.
  • setup-node installs Node and enables npm caching.
  • npm ci installs exactly from the lockfile.
  • The final steps run the project contract: lint, typecheck, test.

The workflow is intentionally boring. Boring is good here. A new teammate can open it and see the gate: "Can this branch install, lint, typecheck, and test?"

The pull request then shows checks beside review:

Pull request #14
Title: Add refresh button to weather widget
 
Checks:
  Checks / checks   passed in 1m 42s
 
Required before merge:
  - approval from 1 reviewer
  - Checks / checks

That is Section 23's human workflow and the machine workflow reinforcing each other. Review asks, "Is this change understandable, maintainable, and worth merging?" CI asks, "Do the repeatable project commands still pass?"

What a failing check is telling you

A failing CI run is not a moral event. It is a saved terminal transcript from a clean machine.

Checks / checks
 
✓ npm ci
✓ npm run lint
✕ npm run typecheck
 
src/ui/refreshButton.ts:18:21 - error TS2345:
Argument of type 'WeatherData | undefined' is not assignable to parameter of type 'WeatherData'.

That failure is useful because it happened before merge. It also happened somewhere neutral: not only on your laptop, not only in your editor, not only with your cached files.

The right response is ordinary:

$ npm run typecheck
$ git add src/ui/refreshButton.ts
$ git commit -m "Guard refresh before weather data loads"
$ git push

The pull request updates, CI runs again, and reviewers can see both the fix and the new result.

Use CI for checks that should be true for every shared change. Keep the first gate focused:

  • npm run lint for suspicious code patterns.
  • npm run typecheck for TypeScript promises.
  • npm test for automated behavior checks.
  • npm run build when build output is part of the deployable contract.

Do not put every slow experiment into the required gate. A twenty-minute required check teaches people to avoid the process. Fast checks create trust.

CD is shipping from the same pipeline

CD usually means one of two related ideas:

  • Continuous delivery: every merged change is kept deployable, and production shipping is a deliberate button, approval, or controlled step.
  • Continuous deployment: every change that passes the pipeline ships automatically.

Beginners do not need to win a terminology argument. The useful idea is: once checks pass, the same workflow can create deployable output and send it to an environment.

An environment is a place the app runs: preview, staging, production. Section 22 used that word for deployment configuration. CI/CD uses it for shipping flow too.

Preview deployments are especially useful with pull requests:

Pull request #14
Title: Add refresh button to weather widget
 
Preview deployment:
  URL: https://weather-widget-pr-14.example.app
  Commit: d0f6a22 Tighten refresh button label
  Status: ready
 
Checks:
  Checks / checks   passed

Now review can include the running app, not just the diff. A reviewer can click the preview URL, try the refresh button, and leave a comment tied to the exact branch.

A production shipping flow is stricter:

Production deployment
 
Source branch: main
Commit: 42b7c91 Merge pull request #14 from add-refresh-button
Checks: passed
Approval: required from @weather-team/release
Environment: production

That shape keeps production special without making it mysterious. The branch rule says only main can deploy. The required checks say broken code cannot ship quietly. The approval says a human still decides when production changes happen.

A deployment workflow shape

The exact deploy command depends on where the app lives. The durable structure looks like this:

name: Deploy
 
on:
  pull_request:
  push:
    branches: [main]
 
jobs:
  checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test
      - run: npm run build
 
  preview:
    needs: checks
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    environment: preview
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run build
      - run: npm run deploy:preview
 
  production:
    needs: checks
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run build
      - run: npm run deploy:production

This is a shape, not a command to paste blindly. deploy:preview and deploy:production are project-specific scripts. A static site host, a serverless platform, and a container setup all deploy differently.

The important pattern is stable:

  1. Build and check first.
  2. Preview pull requests.
  3. Ship production only from main.
  4. Put production behind environment rules or approvals when the project needs them.
  5. Keep secrets in the CI/CD provider's secret store, not in source files.

What automation should not do

Automation can run commands. It cannot decide product taste, judge maintainability, understand every user risk, or notice that a requirement was misunderstood. That remains review work.

The healthy split looks like this:

Automation handles:
  install
  lint
  typecheck
  test
  build
  deploy mechanics
 
Humans handle:
  intent
  design tradeoffs
  naming clarity
  product behavior
  rollout judgment
  whether the tests are the right tests

When CI fails, fix the code or the check. When review finds a risk CI cannot see, improve the code, add a test if the risk is repeatable, and let automation guard that lesson next time.

That closes the Git and collaboration section: snapshots, branches, pull requests, review, conventions, and automation. Next section adds AI to the developer workflow, but the rule stays the same: a tool can speed up work, while human review and automated checks keep the work accountable.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion