Challenge 3: Write a Health Check With Rollback Logic — Possible Solution ==================================================================== // health-check.ts import { z } from "zod"; const ConfigSchema = z.object({ NODE_ENV: z.enum(["development", "production", "test"]), DATABASE_URL: z.string().min(1), }); function loadConfig(env: NodeJS.ProcessEnv = process.env) { return ConfigSchema.parse(env); // throws if invalid — Chapter 4's fail-fast pattern } interface Database { ping(): Promise; } async function healthCheck(db: Database): Promise { try { loadConfig(); // confirms config is valid await db.ping(); // confirms the database is actually reachable return true; } catch { return false; } } // --- deploy.ts: a small deployment script using the health check --- interface DeploymentTarget { deployVersion(version: string): Promise; rollbackToPreviousVersion(): Promise; } async function deployWithRollback( target: DeploymentTarget, db: Database, newVersion: string, maxHealthCheckAttempts = 5 ): Promise { await target.deployVersion(newVersion); for (let attempt = 1; attempt <= maxHealthCheckAttempts; attempt++) { const healthy = await healthCheck(db); if (healthy) { console.log(`Deployment of ${newVersion} is healthy.`); return; } console.warn(`Health check failed (attempt ${attempt}/${maxHealthCheckAttempts})`); await new Promise((resolve) => setTimeout(resolve, 2000)); // brief wait before retrying } console.error(`Deployment of ${newVersion} failed health checks — rolling back.`); await target.rollbackToPreviousVersion(); throw new Error(`Deployment of ${newVersion} failed and was rolled back`); } WHY THIS WORKS -------------- - healthCheck() reuses the exact ConfigSchema.parse() pattern from Chapter 4 — if the new version's config is somehow invalid (a missing env var in the new deploy, for instance), the health check fails immediately rather than the app crashing on its first real request. - db.ping() confirms an actual dependency is reachable, not just that the process started — a health check that only checks "did the process boot" misses the far more common failure mode of a reachable process with a broken downstream connection. - deployWithRollback() retries the health check a few times (deploys can take a moment to become fully ready) before giving up and calling rollbackToPreviousVersion() — turning a failed deploy into an automatic, fast recovery instead of a page at 3am asking a human to intervene.