The Automation Mindset: If You Do It Twice, Script It
Last Tuesday, I ran a database migration, tested 3 API endpoints, checked the Stripe webhook logs, verified the CI pipeline was green, and deployed to production. Total time: 4 minutes.
It used to take 45.
The difference isn't that I got faster at clicking buttons. It's that I stopped clicking buttons entirely.
The Rule
If I do something manually twice, I automate it the third time.
Not "when I have time." Not "next sprint." The third time. Because the fourth time is coming, and the fifth, and the hundredth.
My Automation Stack
Deploy Script (replaced 12 manual steps)
```bash #!/bin/bash
deploy.sh — one command to deploy safely
set -e # Exit on any error
echo "Running pre-deploy checks..." npm run lint npm run test npm run build
echo "Checking production health..." curl -sf https://api.sageideas.dev/health > /dev/null || { echo "Production is already unhealthy. Aborting." exit 1 }
echo "Deploying..." git push origin main
echo "Waiting for Vercel deploy..." sleep 30
echo "Verifying deployment..." curl -sf https://api.sageideas.dev/health > /dev/null || { echo "POST-DEPLOY HEALTH CHECK FAILED" exit 1 }
echo "Deploy successful." ```
This script replaced a checklist I used to follow manually: lint, test, build, check prod health, push, wait, verify. Now it's one command.
Database Backup Verification (replaced a weekly manual check)
```bash #!/bin/bash
verify-backup.sh — runs as a weekly cron job
BACKUP_AGE=$(supabase db dump --dry-run 2>&1 | grep "Last backup" | awk '{print $NF}')
if [ "$BACKUP_AGE" -gt 24 ]; then echo "WARNING: Last backup was $BACKUP_AGE hours ago" | mail -s "Backup Alert" sage@sageideas.org fi ```
I used to manually check backup status every Monday morning. Now a cron job checks every 6 hours and emails me only if something is wrong.
New Project Setup (replaced 30 minutes of boilerplate)
```bash #!/bin/bash
new-project.sh — scaffolds a new project with my standards
PROJECT_NAME=$1
npx create-next-app@latest $PROJECT_NAME --typescript --tailwind --app --eslint cd $PROJECT_NAME
Add my standard config files
cp ~/.templates/.env.example . cp ~/.templates/.github/workflows/ci.yml .github/workflows/ci.yml cp ~/.templates/lib/config.ts lib/config.ts cp ~/.templates/.prettierrc .
Initialize git with conventional commit hook
npx husky init echo 'npx commitlint --edit $1' > .husky/commit-msg
echo "Project $PROJECT_NAME created with CI, linting, and commit hooks." ```
Every new project starts with CI, linting, and commit hooks. No "I'll add those later" — they're there from the first commit.
The ROI of Automation
I track time saved by my automations:
| Automation | Frequency | Time Before | Time After | Annual Savings |
|---|---|---|---|---|
| Deploy script | 5x/week | 15 min | 1 min | 60 hours |
| Backup verification | Daily | 5 min (manual check) | 0 min | 20 hours |
| Project setup | 2x/month | 30 min | 2 min | 11 hours |
| Test data generation | 3x/week | 20 min | 1 min | 48 hours |
| SSL cert monitoring | Continuous | Manual check | Automated alert | 5 hours |
Total: ~144 hours saved per year. That's 18 working days. Almost a full month of engineering time recovered by scripts that took a few hours each to write.
When NOT to Automate
Not everything should be automated. My rules:
- Don't automate things you do once. A one-time data migration doesn't need a reusable script.
- Don't automate things that change constantly. If the process changes every week, the script will need maintenance every week.
- Don't automate critical decisions. Automated deploys: yes. Automated database drops: absolutely not.
The goal isn't to automate everything. The goal is to automate the stuff that's boring, repetitive, and error-prone — freeing your brain for the stuff that actually requires thinking.
The Mindset Shift
Junior engineers think "I should do this task." Senior engineers think "How do I make sure nobody ever has to do this task again?"
That shift — from executing work to eliminating work — is what separates operators from builders.