Manually deploying code to production by SSH-ing into a server and running git pull is a recipe for disaster. CI/CD (Continuous Integration / Continuous Deployment) automates the entire process: every code push triggers tests, builds, and deployments — consistently, reliably, and without human error.
CI vs CD
- Continuous Integration (CI): Automatically build and test code every time a developer pushes changes. Catches bugs early.
- Continuous Delivery (CD): Automatically prepare code for release. Every commit that passes tests is deployment-ready.
- Continuous Deployment: Automatically deploy every commit that passes tests to production. No manual approval step.
A Typical CI/CD Pipeline
Pipeline Stages
1. 📥 Source → Developer pushes code to Git 2. 🔨 Build → Install dependencies, compile TypeScript, bundle assets 3. 🧪 Test → Run unit tests, integration tests, linting 4. 🔍 Analysis → Code quality checks, security scanning 5. 📦 Package → Build Docker image or create deployment artifact 6. 🚀 Deploy → Push to staging, then production 7. ✅ Verify → Health checks, smoke tests, monitoring alerts
GitHub Actions Example
.github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
echo "Deploying to production..."
# Your deployment script here
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
Deployment Strategies
- Rolling Deployment: Gradually replace old instances with new ones. Zero downtime, but both versions run simultaneously during the rollout.
- Blue-Green Deployment: Maintain two identical environments. Deploy to the inactive one, then switch traffic. Instant rollback by switching back.
- Canary Deployment: Route a small percentage (e.g., 5%) of traffic to the new version. If metrics look good, gradually increase to 100%.
- Feature Flags: Deploy new code behind a flag. Enable for specific users or percentages without redeploying.
Testing in CI
Test Pyramid
/ E2E Tests \ ← Slow, expensive, few
/ Integration Tests \ ← Medium speed, moderate count
/ Unit Tests \ ← Fast, cheap, many
Run order in CI:
1. Lint (fastest) → catch style issues
2. Unit tests → catch logic bugs
3. Integration tests → catch wiring bugs
4. E2E tests → catch user-flow bugs
Best Practices
- Keep pipelines fast — aim for under 10 minutes. Parallelize tests, cache dependencies.
- Fail fast — run the fastest checks (lint, type-check) first.
- Never skip tests to deploy faster. If tests are too slow, optimize them.
- Use secrets management — never hardcode credentials in pipeline configs.
- Make deployments boring — if deploying is scary, you're not doing it often enough.
- Always have a rollback plan — one-click rollback to the previous version.
Try Our Free Developer Tools
Minify and optimize your code before deployment.