It was a Sunday, and Shreyansh had exactly one job: get a new laptop ready for work by lunch.
He opened a terminal, typed git clone, and watched the repo drop onto his machine. Simple enough. Then came the real work.
npm install — fine, that one runs itself. But then the README asked for a specific Node version, so he opened a version manager and switched. Then it wanted three environment variables, so he dug through an old Slack thread to find them. Then a database needed to exist locally, so he installed it, started the service, and typed out a handful of CREATE TABLE statements from memory because the schema file was buried two folders deep. Then a .env.example file needed copying and renaming — a step so small it was almost insulting, and yet he forgot it twice.
By the time everything ran, it was well past noon. Nothing about the process was hard. It was just long, repetitive, and full of little steps that lived only in his memory or in someone else's head.
That evening, Shreyansh decided the next new laptop would not cost him a morning.
The First Script
He opened a blank file and named it setup.sh. Not because it was clever, but because it was the most boring, obvious name he could pick — anyone opening the project folder would know exactly what it did.
He started small:
#!/bin/bash
npm install
One line. It didn't solve much on its own, but it proved the idea: a script is just a list of commands that a human would normally type one at a time, written down so the computer can type them instead.
Then he added the next annoying step — copying the environment file:
if [ ! -f .env ]; then
cp .env.example .env
echo "Created .env — remember to fill in your values"
fi
That if check mattered. He'd learned the hard way, on an older project, that a script which blindly overwrites files is worse than no script at all — it can wipe out someone's real configuration in half a second. A good setup script is polite. It checks before it acts.
Teaching It to Notice Things
The database was the part he dreaded most, so he tackled it next.
if ! command -v postgres &> /dev/null; then
echo "Postgres not found — installing..."
brew install postgresql
fi
brew services start postgresql
createdb myapp_dev 2>/dev/null || echo "Database already exists, skipping"
Each line was small, but together they did something bigger: the script could now look at a machine, notice what was missing, and fix only that. It didn't matter if Postgres was already installed — the script would check first and skip the step, the same way a careful person would.
He added the schema next, so a fresh database wasn't just empty but actually usable:
psql myapp_dev -f schema.sql
By the end of the evening, setup.sh was maybe thirty lines long. Not clever, not fancy — just a record of every small decision he used to make by hand, written down once so he'd never have to make them again.
The Second Laptop
Three weeks later, a new teammate joined. Shreyansh sent over the repo and one instruction:
chmod +x setup.sh && ./setup.sh
Eleven minutes later, she messaged back: "It's running. Weirdly satisfying to watch."
No Slack thread. No hunting for a schema file. No forgotten .env copy. The script didn't know anything Shreyansh didn't know — it just remembered it more reliably than he did, and it never got tired of typing the same commands.
That was the real shift. The script wasn't saving time so much as it was saving attention. Every manual step is a place where someone can get distracted, mistype, or skip something without noticing. A script either runs the step or it doesn't — there's no in-between where it "forgot," the way a tired person at 11 AM might.
What the Script Actually Bought Him
Looking back, the thirty lines in setup.sh were doing four separate jobs at once, even though it didn't feel like it while he was writing them:
- A record. The script was the real documentation. Not a README that could drift out of date, but something that either worked or loudly didn't.
-
A safety net. The
ifchecks meant it was safe to run twice, on a clean machine or a messy one, without asking first "wait, will this break anything?" - A shared starting line. Everyone who ran it ended up in the exact same place — same dependencies, same database state, same environment file — instead of five slightly different setups that "mostly" matched.
- A found half-day. Multiply eleven minutes by every new machine, every fresh clone, every time someone wiped their laptop and started over. The minutes were small. The total wasn't.
The Part People Skip
Shreyansh almost stopped there, but one more habit made the difference between a script people trusted and one they quietly avoided: he made it talk.
echo "→ Installing dependencies..."
npm install
echo "✓ Dependencies installed"
echo "→ Setting up database..."
createdb myapp_dev 2>/dev/null || echo " (already exists)"
echo "✓ Database ready"
echo ""
echo "All set. Run 'npm start' to begin."
A script that runs silently for two minutes feels like it might have frozen. A script that narrates itself — even in short, plain lines — feels like it's under control, and so does the person running it. That small bit of feedback was the difference between a teammate watching calmly and a teammate opening a second terminal to check if anything was actually happening.
Top comments (0)