Deployment & DevOps
From Code to Production
Open interactive version (quiz + challenge)Real-world analogy
Writing code is like building a rocket in your garage. Deployment is launching it into orbit — you need a launchpad (server), enough fuel for the journey (scaling), a pre-flight checklist that never changes (CI/CD), and mission control watching every sensor (monitoring)!
What is it?
Deployment is the process of getting your code from your laptop to a server where users can access it. DevOps practices (CI/CD, containers, monitoring) make this reliable, repeatable, and automated.
Real-world relevance
Netflix deploys thousands of times per day. Amazon deploys every 11.7 seconds. Modern CI/CD pipelines make this possible — push code, let the robots handle the rest.
Key points
- CI/CD Pipeline — Continuous Integration and Continuous Deployment automates your entire workflow: push code to Git, automated tests run, linting checks pass, and if everything succeeds, the app deploys automatically to production. No manual steps, no human error. GitHub Actions, GitLab CI, and CircleCI are popular CI/CD platforms.
- Environment Variables — Each deployment environment (development, staging, production) needs different configuration: database URLs, API keys, feature flags, and log levels. Store these as environment variables, never hardcode them. Your CI/CD platform injects the right values for each environment automatically during deployment.
- Container Orchestration — Docker Compose manages multi-container apps on a single server. For larger scale, Kubernetes orchestrates containers across many servers: auto-scaling when traffic spikes, restarting crashed containers, rolling out updates with zero downtime. Start with Docker Compose and graduate to Kubernetes when your app demands it.
- Monitoring & Logging — Production apps need observability: structured logging with tools like Winston or Pino, error tracking with Sentry, performance monitoring with Datadog or New Relic, and uptime monitoring with Pingdom. If something breaks at 3 AM, automated alerts notify your team immediately via Slack, email, or PagerDuty.
- Scaling — When traffic grows beyond what one server can handle, scale horizontally by adding more instances behind a load balancer. The load balancer distributes incoming requests across all instances evenly. Make your app stateless (store sessions in Redis, files in S3) so any instance can handle any request interchangeably.
- SSL/HTTPS — Secure Your App — Production apps MUST use HTTPS to encrypt data in transit. Use Let's Encrypt for free SSL certificates, or your cloud provider's certificate manager. Never transmit passwords or tokens over plain HTTP — it's like sending postcards instead of sealed letters.
- Secrets Management — Never hardcode API keys or passwords in your code. Use environment variables (.env files locally, secret managers in production). Tools like AWS Secrets Manager, HashiCorp Vault, or even GitHub Secrets keep your sensitive data safe and separate from code.
- Blue-Green & Canary Deployments — Blue-green deployment runs two identical environments: blue (current) and green (new version). Switch traffic to green once verified. Canary deployment routes a small percentage of traffic (like 5%) to the new version first, then gradually increases. Both strategies minimize risk and enable instant rollback if issues arise.
- Infrastructure as Code — Define your servers, databases, and networking in code files (Terraform, Pulumi, or AWS CDK) instead of clicking through cloud consoles. Version control your infrastructure alongside your application code. Recreate entire environments from scratch with a single command — reproducible, auditable, and disaster-proof.
Code example
# 1. GitHub Actions CI/CD 🤖
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: pnpm install
- run: pnpm test
- run: pnpm build
deploy:
needs: test # Only deploy if tests pass!
runs-on: ubuntu-latest
steps:
- run: docker build -t myapp .
- run: docker push myregistry/myapp
- run: ssh server "docker pull && docker compose up -d"
# 2. Environment variables — never hardcode secrets! 🔐
# .env (NEVER commit this file!)
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=super-secret-key-change-in-production
REDIS_URL=redis://localhost:6379
// In NestJS — read env vars safely:
@Injectable()
export class AppConfigService {
get databaseUrl(): string {
return process.env.DATABASE_URL;
}
}
// 3. Health check endpoint — monitoring calls this 🏥
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
};
}
}Line-by-line walkthrough
- 1. 1. GitHub Actions CI/CD 🤖
- 2. .github/workflows/deploy.yml
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27. 2. Environment variables — never hardcode secrets! 🔐
- 28. .env (NEVER commit this file!)
- 29.
- 30.
- 31.
- 32.
- 33. In NestJS:
- 34. Decorator that adds metadata or behavior
- 35. Exporting for use in other files
- 36.
- 37. Returning a value
- 38. Closing block
- 39. Closing block
- 40.
- 41. 3. Health check endpoint 🏥
- 42. Decorator that adds metadata or behavior
- 43. Exporting for use in other files
- 44. Decorator that adds metadata or behavior
- 45.
- 46. Returning a value
- 47.
- 48.
- 49.
- 50.
- 51. Closing block
- 52. Closing block
- 53. Closing block
Spot the bug
const app = await NestFactory.create(AppModule);
app.listen(3000);
console.log("Running on port 3000");Need a hint?
Should the port be hardcoded? And is app.listen being awaited?
Show answer
Two issues: 1) Port is hardcoded instead of using process.env.PORT || 3000. 2) app.listen() should be awaited. Fix: await app.listen(process.env.PORT || 3000);
Explain like I'm 5
Writing code is like building a paper airplane at your desk. Deployment is launching a real airplane! You need an airport (server), extra planes if lots of people want to fly (scaling), a checklist the pilot follows every single time (CI/CD), and a control tower watching the sky (monitoring). Going from 'I made a paper plane' to 'I fly passengers across the world!'
Fun fact
Amazon's deployment system handles over 150 million deployments per year. That's about 285 deployments per MINUTE! And you thought pushing to main was stressful... 😅
Hands-on challenge
Design a CI/CD pipeline with 3 stages: (1) Lint + Type-check (fail fast on code quality), (2) Unit tests + Integration tests in parallel, (3) Build Docker image and deploy to staging — but ONLY on the main branch. Add a Slack notification that reports whether the pipeline passed or failed. Draw the pipeline as a flowchart first!
More resources
- Vercel Documentation (Vercel Official)
- GitHub Actions Documentation (GitHub Official)
- CI/CD in 100 Seconds (Fireship)