107
LVL 04 — SENIOR-IN-TRAININGSESSION 107DAY 107

ENVIRONMENT MANAGEMENT

🎫 PIXELCRAFT-093
🔧DevOps | 🟡 Medium | Priority: 🟠 High

A developer tested against production data and accidentally deleted 200 user images. We need proper separation. Set up dev / staging / production environments with proper isolation. PR preview deploys. Staging mirrors production structure.
CONCEPTS.UNLOCKED
🔧
Multiple Environments
Development → Staging → Production. Each environment is isolated: own database, own config, own URL. Bugs caught in dev never touch staging. Bugs caught in staging never touch production.
⚙️
Env-Specific Configuration
.env.development, .env.staging, .env.production. Same code, different config. Dev uses localhost:27017. Staging uses staging-cluster.mongodb.net. Production uses prod-cluster. Code never knows which.
🌱
Database Seeding
Populate dev/staging with realistic test data. Seed scripts create users, images, filters — enough data to test properly, none of it real. Reset anytime with one command.
🪞
Staging = Production Mirror
Same infrastructure, same config, different data. If it works on staging, it works on production. Same Node version, same DB version, same Redis version. Catch config bugs before they hit users.
👀
Preview Deployments
Every PR gets its own temporary URL. Open a PR → Vercel deploys a preview → reviewer clicks the link → tests on a real URL. Merge → preview deleted. No staging bottleneck.
HANDS-ON.TASKS
01
Environment Configuration Files
# .env.development NODE_ENV=development DATABASE_URL=mongodb://localhost:27017/pixelcraft-dev REDIS_URL=redis://localhost:6379 API_URL=http://localhost:3001 FRONTEND_URL=http://localhost:5173 JWT_SECRET=dev-secret-not-for-prod SENTRY_DSN= LOG_LEVEL=debug # .env.staging NODE_ENV=staging DATABASE_URL=mongodb+srv://staging-cluster.mongodb.net/pixelcraft-staging REDIS_URL=redis://staging-redis:6379 API_URL=https://api-staging.pixelcraft.dev FRONTEND_URL=https://staging.pixelcraft.dev JWT_SECRET=${{ secrets.STAGING_JWT }} SENTRY_DSN=https://xxx@sentry.io/staging LOG_LEVEL=info # .env.production NODE_ENV=production DATABASE_URL=mongodb+srv://prod-cluster.mongodb.net/pixelcraft REDIS_URL=redis://prod-redis:6379 API_URL=https://api.pixelcraft.dev FRONTEND_URL=https://pixelcraft.dev JWT_SECRET=${{ secrets.PROD_JWT }} SENTRY_DSN=https://xxx@sentry.io/prod LOG_LEVEL=warn
02
Config Loader with Validation
// lib/config.ts import { z } from 'zod'; const envSchema = z.object({ NODE_ENV: z.enum([ 'development', 'staging', 'production' ]), DATABASE_URL: z.string().url(), REDIS_URL: z.string(), API_URL: z.string().url(), JWT_SECRET: z.string().min(32), SENTRY_DSN: z.string().optional(), LOG_LEVEL: z.enum([ 'debug', 'info', 'warn', 'error' ]), }); export const config = envSchema.parse(process.env); // App crashes on startup if config // is invalid. Better than crashing // 3 hours later on the first request // that needs DATABASE_URL.
03
Database Seeding
// scripts/seed.ts import { faker } from '@faker-js/faker'; async function seed() { console.log('🌱 Seeding database...'); const db = await connectDB(); // Clear existing data await db.dropDatabase(); // Create test users const users = Array.from( { length: 20 }, () => ({ name: faker.person.fullName(), email: faker.internet.email(), password: await hash('test123'), createdAt: faker.date.past(), })); await db.collection('users') .insertMany(users); // Create test images const images = Array.from( { length: 200 }, (_, i) => ({ title: faker.lorem.words(3), userId: users[i % 20]._id, url: faker.image.url(), public: Math.random() > 0.3, filters: { brightness: 50 }, createdAt: faker.date.recent(), })); await db.collection('images') .insertMany(images); console.log( '✅ Seeded 20 users, 200 images'); } // npm run seed → fresh test data
04
CI/CD Per Environment
# Updated CI pipeline: # # Pull Request: # → lint + test + build # → deploy PREVIEW (unique URL) # → comment PR with preview link # # Merge to main: # → lint + test + build # → deploy to STAGING # → run E2E tests against staging # → manual approval gate # → deploy to PRODUCTION # # Each step has its own env vars. # Staging JWT ≠ Production JWT. # Staging DB ≠ Production DB. # Complete isolation.
05
Safety Guards
// Prevent dev from hitting prod DB: // 1. Network isolation: // Dev machine → can't reach prod DB // (IP whitelist on Atlas) // 2. Color-coded terminal prompts: if (config.NODE_ENV === 'production') { console.log( '\x1b[41m 🔴 PRODUCTION \x1b[0m'); } else if ( config.NODE_ENV === 'staging') { console.log( '\x1b[43m 🟡 STAGING \x1b[0m'); } else { console.log( '\x1b[42m 🟢 DEVELOPMENT \x1b[0m'); } // 3. Destructive action guards: if (config.NODE_ENV === 'production') { app.delete('/api/images/all', (req, res) => { res.status(403).json({ error: 'Bulk delete disabled' + ' in production', }); }); }
06
Close the Ticket
git switch -c devops/PIXELCRAFT-093-environments git add .env.* lib/config.ts scripts/seed.ts git commit -m "Add dev/staging/prod environments (PIXELCRAFT-093)" git push origin devops/PIXELCRAFT-093-environments # PR → Preview deploy → Review → Merge ✅
CS.DEEP-DIVE

Environment isolation is a principle of defense in depth.

Multiple barriers between a mistake and production damage. Each barrier catches different classes of errors.

// Defense in depth layers:

Layer 1: Local development
  Catches: syntax, logic, types
  Cost of failure: ~0

Layer 2: PR preview
  Catches: visual, integration bugs
  Cost of failure: ~0

Layer 3: CI tests
  Catches: regressions, API contract
  Cost of failure: ~0

Layer 4: Staging
  Catches: config, infra, data bugs
  Cost of failure: low

Layer 5: Feature flags (canary)
  Catches: user-facing issues
  Cost of failure: limited

Layer 6: Production
  Everything passed all layers
  Cost of failure: HIGH

// Each layer = a safety net.
// More layers = fewer prod incidents.
"Environments Lab"
[A]Add a staging data snapshot: periodically copy sanitized production data (scrub PII: emails, passwords, names) to staging. Test with realistic data volumes without risking real user data.
[B]Add a manual promotion gate in CI: after staging E2E tests pass, require a human to click "Deploy to Production" in the GitHub Actions UI. Automated testing + human judgment before prod.
[C]Research: what is "infrastructure as code" (IaC)? How do tools like Terraform, Pulumi, or AWS CDK define environments in code instead of clicking through dashboards? Why is reproducibility important?
REF.MATERIAL
ARTICLE
Heroku / Adam Wiggins
The foundational principle: store config in environment variables. Strict separation of config from code. The industry standard for environment management.
12-FACTORESSENTIAL
ARTICLE
Vercel
How Vercel creates unique preview URLs for every PR: automatic deployment, environment variables, and collaboration workflows.
PREVIEWOFFICIAL
VIDEO
Fireship
Quick guide to environment variables: .env files, secrets management, per-environment config. Why you should never hardcode API keys.
ENVQUICK
ARTICLE
Faker Community
Generate realistic test data: names, emails, addresses, images, dates. The essential library for database seeding and testing.
FAKERTESTING
VIDEO
TechWorld with Nana
Development, staging, production: why you need multiple environments, how to configure them, and common pitfalls to avoid.
DEVOPSTUTORIAL
// LEAVE EXCITED BECAUSE
Three environments, completely isolated. PR preview deploys. Staging mirrors production. Seed data with one command. No more "oops, that was production."