The Deployment Process That Wasted 3 Hours Every Day
Monday morning, 9:00 AM. Deploy day. Same ritual every week:
Pull latest code from 4 repositories
Run tests in each repo
Build Docker images (names always typo'd)
Tag images with version
Push to registry
Update Kubernetes manifests
Apply to clusters (dev, staging, prod)
Wait for rollout
Verify health checks
Update Slack with deployment status
Time: 45 minutes per deployment. Deployments per day: 4. Total: 3 hours of manual, error-prone work.
Mistakes were common:
March 15: Deployed wrong image tag to production (20-minute outage)
April 3: Forgot to update manifest, old version deployed
May 8: Typo in image name, deployment failed at 6 PM
Our DevOps lead: "We need automation. Can't keep doing this manually."
I built a TypeScript CLI tool: deploy-cli. One command:
Result:
Deployments: 45 minutes β 8 minutes
Errors: Common β zero in 6 months
Time saved: 15 hours per week across team
This article shows you how to build production-grade CLI tools with TypeScript.
Project Setup
Initialize Project
Install Dependencies
TypeScript Configuration
Package.json
Building the CLI
Main Entry Point
Type Definitions
Configuration Management
Example config file:
Command Implementation
Utility Functions
Docker Operations
Kubernetes Operations
Test Runner
Notifications
Error Handling
Building and Installing
Build
Local Installation
Now deploy command is available globally:
Distribution
Users install with:
Testing the CLI
Your Challenge
Extend the CLI with new features:
Key Takeaways
Commander.js - build CLI with options and commands
Chalk - colored terminal output
Ora - loading spinners
Type safety - strong typing for all operations
Config files - YAML for configuration
Error handling - custom error classes
Process execution - promisified exec for shell commands
npm bin - make CLI globally installable
What I Learned
Manual deployments took 45 minutes each. 4 per day = 3 hours of tedious work. Plus constant errors - wrong tags, typos, forgotten steps.
I built deploy-cli in 2 days:
Result:
Deployments: 45 minutes β 8 minutes
Errors: zero in 6 months
Time saved: 15 hours per week
TypeScript was essential:
Type-safe options: Can't pass invalid environment
Compile-time checks: Catch errors before running
Autocomplete: IDE knows all available options
Refactoring: Rename functions, find all usages
Before the CLI, deployments were error-prone manual processes. After the CLI, they're automated, consistent, and fast.
The lesson: Repetitive manual tasks are automation opportunities. If you're doing it multiple times a day, build a tool. TypeScript makes that tool reliable and maintainable.
Turn your daily friction into automated tools. Reclaim your time.
In the final article, we'll build a complete production-ready REST API with TypeScript and Express. You'll learn the architecture that serves 2 million requests per day with 99.9% uptime.
// src/config/index.ts
import fs from 'fs';
import path from 'path';
import yaml from 'js-yaml';
import { DeploymentConfig } from '../types';
export function loadConfig(): DeploymentConfig {
const configPath = path.join(process.cwd(), 'deploy.config.yaml');
if (!fs.existsSync(configPath)) {
throw new Error('deploy.config.yaml not found in current directory');
}
const fileContents = fs.readFileSync(configPath, 'utf8');
const config = yaml.load(fileContents) as DeploymentConfig;
validateConfig(config);
return config;
}
function validateConfig(config: DeploymentConfig): void {
if (!config.services || config.services.length === 0) {
throw new Error('No services defined in configuration');
}
if (!config.dockerRegistry) {
throw new Error('Docker registry not specified');
}
for (const service of config.services) {
if (!service.name || !service.repository) {
throw new Error(`Invalid service configuration: ${JSON.stringify(service)}`);
}
}
}