Expert · Deploying and running the app · topic 26 of 34 · 30 min
CI/CD: shipping every change safely
Explanation
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.
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.
# .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 }}- 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.
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.
-- 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;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 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)
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".
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).
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
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.