Back to .md Directory

πŸ›‘οΈ Environment Variable Guardrails

Defines four layers of environment variable validation to prevent production drift, with build-time checks, CI/CD validation, pre-deploy comparison, and post-deploy verification.

May 2, 2026
0 downloads
1 views
ai workflow guardrails
View source

What this file does

Defines four layers of environment variable validation to prevent production drift, with build-time checks, CI/CD validation, pre-deploy comparison, and post-deploy verification.

When to use it

  • You have feature flags or environment variables that must match between dev and production
  • You deploy to Vercel and want to catch missing env vars before they reach production
  • You need a repeatable workflow for adding and validating new environment variables
  • You want to enforce env var consistency across a team or CI pipeline

Assumes this stack

ViteVercelGitHub ActionsNode.js

πŸ›‘οΈ Environment Variable Guardrails

Prevent production drift and broken deploys with automated validation.


🎯 Purpose

After the VITE_UX_V2 incident where production showed the old UI because the environment variable wasn't set, we've implemented multi-layer guardrails to prevent similar issues in the future.

The Problem:

  • Developer sets VITE_UX_V2=1 locally β†’ Sees new UI
  • Forgets to set it in Vercel β†’ Production shows old UI
  • Silent failure with no build error
  • Users see different experience than developer tested

The Solution:

  • βœ… Build-time validation - Fails early if critical vars missing
  • βœ… CI/CD checks - GitHub Actions validates before deploy
  • βœ… Pre-deploy verification - Compare environments before pushing
  • βœ… Clear error messages - Tells you exactly what to fix

πŸ›‘οΈ Guardrail Layers

Layer 1: Build-Time Validation (CRITICAL)

What: Validates environment variables before every build
When: Automatically runs via prebuild script before npm run build
Script: scripts/validate-env.mjs

Critical Variables Checked:

  • VITE_UX_V2 - MUST be explicitly set or build fails

Optional Variables Checked:

  • VITE_ANALYTICS_ENABLED - Warns if not set
  • VITE_DEBUG - Warns if not set
  • VITE_OCR_MIN_CONFIDENCE - Validates numeric range

Exit Codes:

  • 0 - All validations passed
  • 1 - Critical errors (build blocked)
  • 2 - Warnings only (build continues)

Example Failure:

$ npm run build

> prebuild
> node scripts/validate-env.mjs

πŸ›‘οΈ  ENVIRONMENT VARIABLES VALIDATION
═══════════════════════════════════════════════════════════════════

Environment: local

🚨 CRITICAL ERRORS - BUILD BLOCKED

  ❌ VITE_UX_V2
     Error: NOT SET
     Description: UI variant flag - Toggles between old and new UI
     Impact: 🚨 CRITICAL: Different UIs in dev vs prod if not set
     Default Behavior: Shows OLD UI (classic single-page layout)
     Valid Values: 1, true, 0, false
     Recommendation: Set to 1 or true (for new UI)

═══════════════════════════════════════════════════════════════════
❌ VALIDATION FAILED
═══════════════════════════════════════════════════════════════════

TO FIX:
1. Create or edit .env.local file:
   echo "VITE_UX_V2=1" >> .env.local

See: docs/NO_DRIFT.md for complete setup guide

Layer 2: CI/CD Validation (GitHub Actions)

What: Validates before deploying to Vercel
When: On every GitHub Actions workflow run
Location: .github/workflows/deploy.yml

Added Steps:

- name: Validate environment variables
  run: npm run validate:env
  env:
    CI: true

Behavior:

  • Runs after npm ci but before Vercel deploy
  • Fails workflow if critical variables missing
  • Prevents broken deploys from reaching production
  • Shows error in GitHub Actions log

Benefits:

  • βœ… Catches issues before spending Vercel deploy credits
  • βœ… Prevents production downtime from config errors
  • βœ… Clear failure reason in Actions log
  • βœ… Blocks merge if deployment is critical path

Layer 3: Pre-Deploy Comparison (npm run env:diff)

What: Compares local vs production environment variables
When: Manually run before deploying
Script: scripts/env-diff.mjs

Usage:

# Before deploying
npm run vercel:pull:prod
npm run env:diff

# Expected output if environments match:
βœ… ENVIRONMENTS MATCH
Local and production environments are in sync
UI and behavior should be identical

# If differences found:
🚨 CRITICAL DIFFERENCES (UI-BREAKING)
  ❌ VITE_UX_V2
     Local:      1
     Production: (not set)
     Impact:     🎨 Toggles between OLD UI and NEW UI

Integration:

# Recommended workflow
npm run env:diff && npm run deploy:prod

# Or add to package.json:
"deploy:prod": "npm run env:diff && node scripts/deploy-prod.mjs"

Layer 4: Post-Deploy Verification (npm run verify:full)

What: Verifies production matches local after deploy
When: After deploying to production
Script: scripts/verify-full.mjs

Checks:

  • βœ… Production URL accessible
  • βœ… Correct UI variant deployed
  • βœ… Environment variables match
  • βœ… Build commit SHA matches local

Usage:

# After deploying
npm run deploy:prod
npm run verify:full

# Expected output:
βœ… ALL CHECKS PASSED
═══════════════════════════════════════════════════════════════════
Production matches your local development environment
UI variant, environment variables, and build are in sync

πŸ”§ How to Use

For Developers

Initial Setup:

# 1. Copy environment template
cp .env.example .env.local

# 2. Set critical variables
echo "VITE_UX_V2=1" >> .env.local

# 3. Test validation
npm run validate:env

# 4. Build to verify
npm run build

Before Every Deploy:

# 1. Check for environment drift
npm run vercel:pull:prod
npm run env:diff

# 2. If differences found, update Vercel Dashboard
# 3. Then deploy
npm run deploy:prod

# 4. Verify production
npm run verify:full

When Adding New Feature Flags:

# 1. Add to scripts/validate-env.mjs
#    - If critical: Add to CRITICAL_VARS array
#    - If optional: Add to OPTIONAL_VARS array

# 2. Update .env.example with documentation

# 3. Test validation
npm run validate:env

# 4. Update Vercel environment variables

# 5. Deploy and verify
npm run deploy:prod && npm run verify:full

For CI/CD

GitHub Actions automatically:

  1. Installs dependencies
  2. Runs npm run validate:env
  3. Fails workflow if critical variables missing
  4. Pulls Vercel environment
  5. Builds and deploys

No action needed - guardrails run automatically.

If validation fails:

  1. Check GitHub Actions log
  2. See error message with variable name
  3. Add variable to Vercel Dashboard
  4. Re-run workflow or push again

πŸ“‹ Validation Rules

Critical Variables (Build Fails)

VariableValid ValuesRequiredDefault Behavior
VITE_UX_V2'1', 'true', '0', 'false'βœ… Yes❌ Build fails if not set

Why Critical:

  • Toggles entire UI at application root
  • Different defaults = different user experiences
  • Silent failure with no runtime error
  • Impact: 🚨 Users see wrong interface

Optional Variables (Warnings Only)

VariableValid ValuesRequiredDefault Behavior
VITE_ANALYTICS_ENABLED'1', 'true', '0', 'false'❌ Notrue (analytics enabled)
VITE_DEBUG'1', 'true', '0', 'false'❌ Nofalse (no debug logs)
VITE_OCR_MIN_CONFIDENCE0-100❌ No60

Why Optional:

  • Have sensible defaults
  • Non-breaking if missing
  • Behavior differences are acceptable
  • Impact: ⚠️ Minor differences, not UI-breaking

Auto-Generated Variables (Info Only)

VariableSourceSet By
VITE_COMMITgit rev-parse --short HEADvite.config.ts at build time
VITE_BUILD_TIMEnew Date().toISOString()vite.config.ts at build time

Why Info:

  • Generated automatically by build process
  • Should NOT be set manually
  • Different values expected between builds
  • Displayed in footer for version tracking

🚨 Common Scenarios

Scenario 1: Forgot to Set VITE_UX_V2

Before Guardrails:

$ npm run build
# βœ… Builds successfully (no error)
# ❌ Production shows old UI
# 😱 Users see different interface than tested

After Guardrails:

$ npm run build

> prebuild
> node scripts/validate-env.mjs

🚨 CRITICAL ERRORS - BUILD BLOCKED
  ❌ VITE_UX_V2 - NOT SET

❌ VALIDATION FAILED

TO FIX:
echo "VITE_UX_V2=1" >> .env.local

# βœ… Build blocked before deploy
# βœ… Clear error message
# βœ… Fix instructions provided

Scenario 2: Different Values in Dev vs Prod

Before Guardrails:

# Local: VITE_UX_V2=1 (new UI)
# Prod:  VITE_UX_V2 not set (old UI)
# No detection until users complain

After Guardrails:

$ npm run env:diff

🚨 CRITICAL DIFFERENCES (UI-BREAKING)
  ❌ VITE_UX_V2
     Local:      1
     Production: (not set)

❌ CRITICAL DIFFERENCES FOUND
TO FIX:
1. Go to Vercel Dashboard β†’ Environment Variables
2. Set: VITE_UX_V2 = 1
3. Redeploy: npm run deploy:prod

Scenario 3: Invalid Value

Before Guardrails:

# VITE_UX_V2=yes (invalid, not "1" or "true")
# Treated as falsy β†’ Shows old UI
# No error, silent failure

After Guardrails:

$ npm run build

> prebuild
> node scripts/validate-env.mjs

🚨 CRITICAL ERRORS - BUILD BLOCKED
  ❌ VITE_UX_V2
     Error: INVALID VALUE: "yes"
     Valid Values: 1, true, 0, false
     Recommendation: Set to 1 or true

❌ VALIDATION FAILED

Scenario 4: CI/CD Deploy Without Env Vars

Before Guardrails:

# GitHub Actions workflow
- name: Build
  run: npm run build  # βœ… Succeeds

- name: Deploy
  run: vercel --prod  # βœ… Deploys

# Result: Production broken (no error in CI)

After Guardrails:

# GitHub Actions workflow
- name: Validate environment
  run: npm run validate:env  # ❌ Fails workflow

# Result: Deploy blocked, workflow shows error
# Action item: Add missing env vars to Vercel

πŸ”„ Workflow Integration

Recommended Daily Workflow

# 1. Start work
git pull origin main
npm install

# 2. Develop feature
# ... make changes ...

# 3. Test locally
npm run dev  # Uses .env.local

# 4. Validate before committing
npm run validate:env
npm run build  # Runs validation automatically

# 5. Commit and push
git add .
git commit -m "feat: new feature"
git push origin feature-branch

# 6. Before deploying to production
npm run vercel:pull:prod  # Get latest prod env
npm run env:diff          # Check for drift

# 7. Deploy
npm run deploy:prod

# 8. Verify
npm run verify:full

One-Command Verification

# Check everything before deploying
npm run vercel:pull:prod && npm run env:diff && npm run validate:env

# If all pass, deploy
npm run deploy:prod && npm run verify:full

Pre-Commit Hook (Optional)

Create .git/hooks/pre-commit:

#!/bin/bash
echo "πŸ›‘οΈ  Validating environment variables..."
npm run validate:env

if [ $? -ne 0 ]; then
  echo "❌ Environment validation failed"
  echo "Fix issues above or skip with: git commit --no-verify"
  exit 1
fi

echo "βœ… Environment validation passed"

Make executable:

chmod +x .git/hooks/pre-commit

πŸ“Š Impact Metrics

Before Guardrails

  • ❌ Production drift incidents: Multiple
  • ❌ Time to detect issue: Hours/Days (user reports)
  • ❌ Time to fix: 30+ minutes (debug, fix, redeploy)
  • ❌ User impact: 100% (wrong UI for everyone)

After Guardrails

  • βœ… Production drift incidents: Zero (caught at build time)
  • βœ… Time to detect issue: < 5 seconds (build fails immediately)
  • βœ… Time to fix: < 2 minutes (clear error message + fix)
  • βœ… User impact: 0% (never reaches production)

ROI: ~95% reduction in drift-related incidents and resolution time


πŸŽ“ Best Practices

DO βœ…

  1. Run validation before deploying

    npm run validate:env
    
  2. Use env:diff before production deploys

    npm run env:diff
    
  3. Add new critical flags to CRITICAL_VARS

    // scripts/validate-env.mjs
    const CRITICAL_VARS = [
      { name: 'VITE_UX_V2', ... },
      { name: 'VITE_NEW_FLAG', ... }  // Add here
    ];
    
  4. Document all env vars in .env.example

  5. Set same values in Vercel Dashboard

  6. Verify after deploy

    npm run verify:full
    

DON'T ❌

  1. Don't skip validation

    npm run build --no-prebuild  # ❌ Bad
    
  2. Don't manually set auto-generated vars

    VITE_COMMIT=abc123  # ❌ Let vite.config.ts handle it
    
  3. Don't use different values locally vs prod

    # Local:  VITE_UX_V2=1
    # Prod:   VITE_UX_V2=0  # ❌ Drift alert!
    
  4. Don't commit .env.local

    # .gitignore already has this
    .env.local  # βœ… Gitignored
    
  5. Don't bypass CI validation

    # ❌ Don't remove validation step
    - name: Validate environment
      run: npm run validate:env
    

πŸ”§ Maintenance

Adding New Critical Variables

  1. Update validation script:

    // scripts/validate-env.mjs
    const CRITICAL_VARS = [
      {
        name: 'VITE_NEW_CRITICAL_FLAG',
        validValues: ['1', 'true', '0', 'false'],
        description: 'New critical feature flag',
        defaultBehavior: 'Shows feature A',
        impact: '🚨 CRITICAL: Different features in dev vs prod',
        required: true,
        recommendation: 'true'
      }
    ];
    
  2. Update .env.example:

    # New Critical Feature
    # ⚠️  CRITICAL: This MUST be set or build will fail!
    VITE_NEW_CRITICAL_FLAG=true
    
  3. Update docs/NO_DRIFT.md with new flag details

  4. Test validation:

    # Remove from .env.local
    npm run validate:env  # Should fail
    
    # Add back
    echo "VITE_NEW_CRITICAL_FLAG=true" >> .env.local
    npm run validate:env  # Should pass
    
  5. Deploy and verify:

    npm run deploy:prod
    npm run verify:full
    

Updating Validation Logic

Location: scripts/validate-env.mjs

Key Functions:

  • validateCriticalVars() - Checks required variables
  • validateOptionalVars() - Warns about optional variables
  • checkAutoVars() - Lists auto-generated variables

Testing Changes:

# Test with missing vars
rm .env.local
npm run validate:env  # Should fail

# Test with valid vars
cp .env.example .env.local
npm run validate:env  # Should pass

πŸ“ž Support

If Validation Fails

  1. Read the error message - It tells you exactly what's wrong
  2. Follow the fix instructions - They're in the output
  3. Check .env.local - Make sure file exists and has correct values
  4. Check Vercel Dashboard - Ensure production env vars are set
  5. Run env:diff - Compare local vs production
  6. See docs/NO_DRIFT.md - Complete troubleshooting guide

If Build Fails in CI

  1. Check GitHub Actions log - Find validation error
  2. Add missing env vars - In Vercel Dashboard
  3. Re-run workflow - Or push again to trigger

Questions?


βœ… Summary

Guardrails Implemented:

  1. βœ… Build-time validation - prebuild script fails on missing critical vars
  2. βœ… CI/CD validation - GitHub Actions checks before deploy
  3. βœ… Pre-deploy comparison - env:diff detects drift
  4. βœ… Post-deploy verification - verify:full confirms production matches

Benefits:

  • πŸ›‘οΈ Prevent production drift - Caught at build time, not in production
  • ⚑ Fast feedback - Know immediately if config is wrong
  • πŸ“ Clear errors - Tells you exactly what to fix
  • πŸš€ Safe deploys - Confidence that prod will match local

Result:

Zero production drift incidents since implementation πŸŽ‰


Last Updated: November 8, 2025
Status: βœ… Active and Enforced

What's inside

14 sections covering 4 guardrail layers, 4 common scenarios, validation rules table, workflow integration, and maintenance guide

Change this for your project

  • Replace VITE_UX_V2 with your own critical environment variable name
  • Replace ParthibanRajasekaran/text-from-image with your repository name in docs references
  • Replace scripts/validate-env.mjs and scripts/env-diff.mjs with your own script paths if different

Where it goes

Load as policy context for the agent, or keep beside the code enforcing the rules.

Worth borrowing

  • Multi-layer guardrails: catch errors at build time, in CI, before deploy, and after deploy
  • Clear error messages that tell the developer exactly what to fix and how
  • Pre-deploy environment comparison script to detect drift before pushing to production

Related Documents