Skip to content

Expert · Deploying and running the app · topic 26 of 34 · 30 min

CI/CD: shipping every change safely

Explanation

CI (continuous integration) means a robot checks every change: it installs, lints, tests and builds the code within minutes. CD (continuous delivery or deployment) means changes that pass are shipped automatically, the same way every time. Add preview deployments for pull requests, safe database migrations, a quick rollback and branch protection, and releasing stops being scary.

CI and CD in plain words

Continuous integration (CI): every time someone pushes code, a fresh machine downloads it, installs the packages, runs the linter (a tool that finds mistakes and style problems), runs the tests and builds the app. If any step fails, you see a red cross within minutes, while the change is still fresh in your mind.

Continuous delivery means every change that passes CI is ready to release with one click. Continuous deployment goes one step further: it is released automatically. Both are called CD. The steps from push to live site together are called the pipeline.

Think of it like this: A good sweet shop tastes every new batch of rasgulla before it goes on the counter. A bad batch never reaches customers. If one slips through, they take it off the counter and put back yesterday's good batch at once, then find out what went wrong. CI is the tasting, CD is putting good batches on the counter, and a rollback is bringing back yesterday's batch.

A workflow with GitHub Actions

GitHub Actions runs pipelines for repositories on GitHub. A workflow is a YAML file in the folder .github/workflows. It says when to run (on), and lists jobs; each job runs its steps in order on a fresh virtual machine called a runner.

output
# .github/workflows/ci-deploy.yml
name: ci-and-deploy
on:
  push:
    branches: [main]
  pull_request:

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: lts/*
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

  deploy:
    needs: check
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    concurrency: production
    steps:
      - name: Ask the host to deploy this commit
        run: curl -fsS -X POST "$DEPLOY_HOOK_URL"
        env:
          DEPLOY_HOOK_URL: ${{ secrets.DEPLOY_HOOK_URL }}
CI runs on every push and pull request. The deploy job runs only for pushes to main, and only after the check job passed.
  • npm ci installs exactly the versions in package-lock.json, so CI uses the same packages as your laptop.
  • needs: check makes deploy wait for the check job; if a test fails, nothing is deployed.
  • if: ... limits deploy to pushes on main, so pull requests are checked but not shipped to production.
  • environment: production lets you keep production secrets separate and even require an approval click.
  • concurrency: production stops two deploys from running at the same time.
  • The deploy hook is a secret URL many hosts give you; calling it starts a deploy. It is stored in GitHub secrets, never in the file.
Tip: Many hosts (Vercel, Netlify, Render and others) can deploy straight from GitHub on every push to main, so you may not need a deploy job at all. Then keep the workflow for CI, and turn on the host's option to wait for GitHub checks, if it has one, so a failing build is never shipped.

Preview deployments

A preview deployment is a temporary copy of the app built from a pull request, with its own URL. Reviewers click through the real change on their phone before it is merged. Frontend hosts make one for every pull request automatically. Point previews at a preview database with test data, never at production.

Database migrations in a release

A migration is a script that changes the database structure, such as adding a column. Run migrations once per release, as a separate step before the new code starts (many hosts have a "pre-deploy" or "release" command for this). Do not run them inside every server at startup: with three API instances, three copies would try to change the table at the same moment.

Make every migration backward compatible: the old code must still work after it runs. During a deploy, old and new code run side by side for a short time, and after a rollback the old code runs against the new database. The safe pattern is expand, then contract: first add the new thing, later remove the old thing.

sql
-- Release 1 (expand): add the new column, copy the data.
-- The new code writes to both body and content, and reads content.
ALTER TABLE notes ADD COLUMN content TEXT;
UPDATE notes SET content = body WHERE content IS NULL;

-- Release 2, days later (contract): no running code uses body any more.
ALTER TABLE notes DROP COLUMN body;
Renaming body to content safely takes two releases. A single RENAME COLUMN would break the old code during the deploy.
Careful: Rolling back the code does not roll back the database. If a migration dropped a column, one click on "rollback" will not bring the data back. Take a backup before any risky migration, and drop columns only in a later release.

Rollback and branch protection

A rollback puts the previous working release back. Most platforms keep old deploys and can redeploy one with a click; you can also git revert the bad commit and push. When users are hurting, roll back first and investigate later.

Branch protection is a GitHub setting (in branch protection rules or rulesets) for main: changes only arrive through pull requests, the CI checks must pass, someone may have to approve, and force pushes are blocked. Then main is always in a state you could deploy.

Watch the lesson

CI/CD: shipping every change safely · 1/7

CI: check every change CD: ship what passes

CI means a robot checks every change: install, lint, test and build. CD means changes that pass are shipped the same way every time.

Prefer a video by a teacher? Find free videos on “github actions ci cd node js deploy tutorial” on YouTube ↗

In real life: A food delivery app ships on a Friday evening

A small team runs a food ordering app for local restaurants. A developer opens a pull request that changes the checkout page. CI lints, tests and builds it in four minutes; the preview link lets the owner try the new page on her phone. After approval it is merged, and the pipeline deploys it. At 8 pm, peak dinner time, orders with coupons start failing. Instead of debugging live, the on-call developer clicks rollback; the old release is back in a minute and orders flow again. The fix and a new test arrive the next morning through the same pipeline.

Practice (0/3 done)

Task 1 · Code · Javascript

Write shouldDeploy({ branch, testsPassed, event }) that returns "production", "preview" or "none". Rules in order: if testsPassed is not true, return "none"; if event is "pull_request", return "preview"; if event is "push" and branch is "main", return "production"; otherwise return "none".

Task 2 · Code · Javascript

Write reviewMigration(sql) that flags changes which are not backward compatible. Split sql on ";", trim each part and skip empty parts. Number the statements from 1. Ignoring upper/lower case, add a message for each statement, checking in this order: "N: drops a table" (contains DROP TABLE), "N: drops a column" (DROP COLUMN), "N: renames a column" (RENAME COLUMN), "N: adds a NOT NULL column without a DEFAULT" (contains ADD COLUMN and NOT NULL but no DEFAULT). Return the array of messages ([] when safe).

Task 3 · Practice

Write a GitHub Actions workflow file for your Notes app repository (.github/workflows/ci-deploy.yml) and turn on branch protection for main. If your host deploys from GitHub by itself, keep only the check job and switch on the host's "wait for checks" option if it has one.

Quick quiz

Question 1

1.What does CI do on every push?

Question 2

2.Why use npm ci instead of npm install in a pipeline?

Question 3

3.You want to rename the notes column body to content without breaking anything. What is the safe way?

Question 4

4.A release breaks checkout at peak time. What should you do first?

Question 5

5.What is a preview deployment?

Read more (free)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: Environment variables and secrets