Difficulty: Intermediate
What is CI/CD? Describe a deployment pipeline you've worked with or would design.
CI (Continuous Integration): Automatically build and test code on every push. Catches bugs early.
CD (Continuous Delivery): Automatically prepare releases for deployment. Human approves.
CD (Continuous Deployment): Automatically deploy to production on every merge. No human step.
A good pipeline: Push → Lint → Test → Build → Deploy to staging → Manual approval → Deploy to production
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: test
ports:
- 5432:5432
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 typecheck
- run: npm test -- --coverage
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
This pipeline runs on every push and PR: installs deps, lints, type-checks, tests with coverage, and builds. If any step fails, the PR can't be merged.
// 1. Rolling Deployment
// Replace instances one at a time
// [v1] [v1] [v1] → [v2] [v1] [v1] → [v2] [v2] [v1] → [v2] [v2] [v2]
// Pro: zero downtime, gradual rollout
// Con: mix of versions temporarily
// 2. Blue-Green Deployment
// Blue (current) ← traffic
// Green (new) ← deploy & test here
// Then switch: Green ← traffic
// If problems: switch back to Blue instantly
// Pro: instant rollback
// Con: needs 2x infrastructure
// 3. Canary Deployment
// Route 5% of traffic to new version
// Monitor errors, latency, business metrics
// If OK: gradually increase to 25% → 50% → 100%
// If problems: route all traffic back to old version
// Pro: catches issues with minimal user impact
// Con: complex routing logic
// 4. Feature Flags (not a deployment strategy, but related)
// Deploy code but control visibility
if (featureFlags.isEnabled('new-checkout', user)) {
renderNewCheckout();
} else {
renderOldCheckout();
}
Start with Blue-Green for simplicity and instant rollback. Move to Canary when you need more confidence in gradual rollouts.
// The 3 pillars of observability:
// 1. LOGGING (what happened)
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.Console()],
});
logger.info('User logged in', { userId: 123, ip: '1.2.3.4' });
logger.error('Payment failed', { orderId: 456, error: err.message });
// 2. METRICS (how much / how fast)
// - Request rate (requests/second)
// - Error rate (% of 5xx responses)
// - Latency (p50, p95, p99 response times)
// - Saturation (CPU, memory, disk usage)
// Tools: Prometheus, Grafana, Datadog
// 3. TRACING (request flow across services)
// Track a request through: API Gateway → Auth → DB → Cache → Response
// Tools: Jaeger, Zipkin, OpenTelemetry
// Health check endpoint
app.get('/health', async (req, res) => {
const dbOk = await checkDatabase();
const cacheOk = await checkRedis();
const status = dbOk && cacheOk ? 200 : 503;
res.status(status).json({ db: dbOk, cache: cacheOk });
});
// Alerting rules:
// - Error rate > 1% for 5 minutes → warning
// - Error rate > 5% for 2 minutes → critical
// - p99 latency > 2s for 10 minutes → warning
// - Health check fails → critical
Good monitoring catches issues before users report them. The health endpoint is used by load balancers and Kubernetes to route traffic away from unhealthy instances.
CI/CD, GitHub Actions, Testing Pipeline, Blue-Green Deploy, Monitoring