The two‑line 'reset‑demo‑user' script that saved a client call — and the day it almost wiped production

How a tiny repo script that reseeds a demo user fixed my last‑minute staging chaos — and the hard lesson I learned when it nearly ran against production.

Written by: Arjun Malhotra

Laptop on a wooden desk with a notebook and coffee
Photo by Brooke Cagle on Unsplash

I was on a 11:00 AM Zoom with the CTO of a Bengaluru payments startup. Demo queued. Staging looked healthy…until I switched users and everything I expected to show (a clean merchant dashboard, two successful transactions, a demo payout) turned into a user’s messy history and an unpaid invoice.

Silence on the other end. My heart did the sprint it does before a production restore. I could have spent ten panicked minutes manually deleting rows, patching balances, resetting the UI, and still not be confident it matched what I wanted to show. Instead I hit a script: ./scripts/reset-demo-user.sh demo@company.com.

Thirty seconds later the dashboard showed exactly what I needed. The CTO smiled. We closed the call. Contract value: roughly ₹50,000 in immediate revenue and a pilot that led to more work.

That script is two real lines (plus a comment and a guard). It lives in every repo I touch now. Here’s what it is, why it matters, and why I nearly deleted production with it.

Why a tiny script beats manual demo prep

Before the script I did three things that always failed me:

What I needed was repeatability, speed, and a safety net. The script gives all three. The idea is embarrassingly simple: keep a small JSON + SQL seed for “demo users” checked into the repo and a two‑line wrapper that loads it into the current environment, wrapped in a transaction and guarded by environment checks.

The core is:

Because it’s in the repo, it evolves with schema changes. Because it’s repeatable, I can run it in 30–60s even on my 15 Mbps work-from-home link. Because it’s a script and not a manual checklist, it avoids the “did I forget to zero the balance?” panic.

What the script looks like (practical bits)

I keep the script short, explicit, and paranoid. Example (conceptual):

#!/usr/bin/env bash set -euo pipefail

safety: only allow when DB host contains “staging”

[[ ”${DATABASE_URL:-}” =~ staging ]] || { echo “Refusing to run: not a staging DB”; exit 1; }

psql “$DATABASE_URL” -v ON_ERROR_STOP=1 <<‘SQL’ BEGIN; — seed: delete ephemeral rows, insert deterministic demo user, recreate token, set balances DELETE FROM transactions WHERE user_id = (SELECT id FROM users WHERE email = ‘demo@company.com’); DELETE FROM sessions WHERE user_id = (SELECT id FROM users WHERE email = ‘demo@company.com’); — …insert statements from checked‑in seed.sql… COMMIT; SQL

Some practical lessons I learned while building this:

These make the script fast and predictable. It also reduces mobile data usage for remote demos: tiny SQL push instead of a 200 MB snapshot.

The failure that changed everything

A few months after the Bengaluru demo I almost deleted production.

I ran the script from my laptop in a hurry. My .env file had DATABASE_URL set to a production read replica by mistake (I switch networks a lot; it’s an annoying, repeated thing). The staging regex check was present but I had refactored the script and accidentally changed the match to allow “prod” for internal testing. Result: the script started running against the wrong host. I caught it because psql printed a host name I didn’t expect — at step 3.

I did not commit any rows, thankfully. I’d wrapped everything in a transaction and the script error left the DB untouched. But we still paid the price: a restore verification, an incident postmortem, about ₹4,500 in cloud egress and consultant time, and 90 minutes of embarrassment answering “How did this happen?” to my manager.

The fix was brutal and obvious:

That failure also changed where the script lives. I stopped placing it in a team member’s home directory or a shared drive and put it in the repo with a small audit trail: a quick CI job that runs the seed in dry-run and commits a hash of the seed.sql each time it changes.

The tradeoffs I accepted

Takeaway

A tiny, well‑guarded script that reseeds a demo user saves more than time. It preserves credibility.

I no longer ship demos on seat‑of‑pants data edits. My one genuine takeaway: if a script can change data, code its paranoia first. The minute you allow convenience to beat safety, you’re one env var away from a real outage.