← Build log

Environment Cleanup Scripts: A Solo Builder's Sanity Saver

June 17, 2025

Building software as a solo developer means wearing every hat - developer, QA, DevOps, support. One challenge that consistently trips up solo builders is environment drift: when your development environment slowly accumulates cruft, old processes, conflicting dependencies, and state that makes debugging a nightmare.

Today I want to share a real example from my VoiceCatcher project where I learned this lesson the hard way, and how I solved it with targeted cleanup scripts.

The Problem: When “It Works On My Machine” Becomes Your Enemy

Picture this: You’re ready to cut a new release. You run your standard build command and… something’s wrong. The GUI doesn’t open. Processes are hanging. Your carefully orchestrated build pipeline is behaving differently than it did yesterday.

This exact scenario happened to me today. I ran npm run start:gui expecting the familiar VoiceCatcher interface to pop up, but instead got a running app with no visible window. The Electron processes were there, hotkeys were registered, settings were loading - but no GUI.

The culprit? Environment drift.

  • Old Electron processes from previous sessions
  • Cached build artifacts pointing to wrong versions
  • Stale Swift binaries in debug mode instead of release
  • Multiple Vite dev servers competing for resources
  • Test processes that never properly cleaned up

As a solo builder, I don’t have a dedicated DevOps team to maintain pristine CI environments. My laptop is my development environment, and it accumulates state over weeks of development iterations.

The Solution: Strategic Cleanup Scripts

Instead of manually hunting down processes and clearing caches every time something went sideways, I created three targeted cleanup scripts:

1. End-User Cleanup Script (env-cleanup.sh)

#!/bin/bash
# For end users who just want a clean slate

echo "🧹 VoiceCatcher Cleanup Utility"

# Check if app is running
if pgrep -f "VoiceCatcher.app" > /dev/null; then
    echo "❌ Please quit VoiceCatcher first"
    exit 1
fi

# Clean user data and settings
rm -rf ~/Library/Application\ Support/VoiceCatcher
rm -rf ~/Library/Caches/VoiceCatcher
rm -rf ~/Library/Preferences/com.voicecatcher.*

echo "✅ VoiceCatcher environment cleaned successfully"

Purpose: Give end users a simple way to reset everything when they encounter issues. No technical knowledge required.

2. Developer Cleanup Script (dev-only-cleanup.sh)

#!/bin/bash
# For developers who need comprehensive environment reset

source scripts/env-cleanup.sh  # Start with user cleanup

# Development-specific cleanup
rm -rf node_modules/.cache
rm -rf dist dist_gui dist_main
rm -rf .next .nuxt build
killall -9 node electron 2>/dev/null || true

# Git state cleanup (with safety checks)
if [ -f .env ]; then
    echo "🔒 Preserving .env file"
fi

echo "🔧 Development environment reset complete"

Purpose: Nuclear option for developers when everything goes wrong. Resets both user state and development artifacts.

3. Build Cleanup Script (build-cleanup.sh)

#!/bin/bash
# Clean Swift binaries and force release builds

echo "🔨 Cleaning Swift build artifacts..."

cd bin/audio-capture
swift package clean
swift package reset

# Force release build
swift build -c release --product voicecatcher-capture

echo "✅ Swift cleanup and release build complete"

Purpose: Ensure Swift binaries are built in release mode and clear any debug artifacts that might interfere with production builds.

The Strategic Value for Solo Builders

Pros: Why These Scripts Matter

1. Debugging Acceleration When something breaks, you can quickly rule out “environment cruft” as the cause. Run the cleanup script, rebuild, and see if the issue persists with a pristine environment.

2. Release Confidence
Before cutting a release, run a clean build to ensure you’re not shipping artifacts that only work because of some accumulated state in your development environment.

3. Onboarding Speed Whether it’s a new contributor or future-you setting up on a new machine, cleanup scripts document exactly what needs to be reset for a working environment.

4. Mental Load Reduction Instead of remembering “kill these 5 processes, clear these 3 caches, rebuild these 2 binaries,” you run one script.

Cons: The Tradeoffs

1. Build Time Impact Cleanup scripts often mean rebuilding everything from scratch. For large projects, this can add significant time to your development cycle.

2. Over-Reliance Risk If you’re running cleanup scripts daily, that’s a symptom of deeper build system issues that should be addressed.

3. State Loss Aggressive cleanup can destroy useful development state - test data, configuration tweaks, cached dependencies that took time to fetch.

4. Maintenance Overhead Cleanup scripts need to evolve with your project. New dependencies, changed directory structures, and new state locations all need updates.

Implementation Lessons Learned

1. Layer Your Cleanup Scripts

Don’t create one monolithic “reset everything” script. Layer them:

  • User-facing: Simple, safe, focuses on user data
  • Developer: Includes build artifacts and development tools
  • Nuclear: Everything including git state (with safeguards)

2. Make Them Discoverable

Add cleanup commands to your package.json:

{
  "scripts": {
    "clean:user": "./scripts/env-cleanup.sh",
    "clean:dev": "./scripts/dev-only-cleanup.sh",
    "clean:build": "./scripts/build-cleanup.sh",
    "clean:nuclear": "npm run clean:dev && npm run clean:build"
  }
}

3. Include Safety Checks

Before destructive operations, check if the app is running, prompt for confirmation, or preserve critical files:

# Don't nuke user data while app is running
if pgrep -f "YourApp" > /dev/null; then
    echo "❌ Please quit YourApp first"
    exit 1
fi

# Preserve environment files
if [ -f .env ]; then
    cp .env .env.backup
fi

4. Document the “Why”

Include comments explaining why each cleanup step exists. Future-you will thank present-you when debugging at 2 AM.

The Real-World Impact

In today’s debugging session, the cleanup scripts saved me hours. Instead of manually hunting down the root cause of why the GUI wasn’t appearing (old processes? cached builds? debug binaries?), I ran:

npm run clean:dev
npm run package:clean

The result? A pristine v0.3.5 build that behaved exactly as expected.

When NOT to Use Cleanup Scripts

During active development: If you’re rebuilding from scratch every time you make a small change, your build system has bigger problems.

For CI/CD: Continuous integration should start with clean environments by default. Cleanup scripts are for persistent development environments.

As a debugging crutch: If the only way to fix issues is “run the cleanup script,” you’re masking underlying problems that need proper fixes.

The Solo Builder’s Perspective

As a solo developer, you can’t afford to spend hours debugging environment issues that larger teams solve with dedicated infrastructure. Cleanup scripts are a force multiplier - they let you quickly eliminate an entire category of problems so you can focus on building features.

But they’re also a discipline tool. Writing cleanup scripts forces you to understand your project’s state dependencies and build artifacts. That understanding makes you a better architect.

The key is balance: powerful enough to solve real problems, but not so convenient that you ignore underlying build system issues.

Takeaways

  1. Environment drift is inevitable for solo builders working on long-lived projects
  2. Layer your cleanup scripts from user-friendly to nuclear options
  3. Include safety checks to prevent accidental data loss
  4. Document the why behind each cleanup step
  5. Use them strategically - not as a daily crutch, but as a debugging accelerator

Have you built cleanup scripts for your projects? What patterns have you found helpful? Let me know on Twitter - I’d love to hear your approaches.


This post is part of my ongoing series about building software as a solo developer. For more insights on architecture decisions, productivity tools, and lessons learned, check out the full blog archive.