← Build log

Icon Management Disaster Recovery: Building Systematic Asset Protection for Electron Apps

Author: Jeremy Geiger
Date: May 2025
Read Time: 15 minutes
Category: Technical Product Management, Asset Management, Build Systems

Executive Summary

Silent asset failures are a product manager’s nightmare. Your build succeeds, your app ships, but users see broken images where critical UI elements should be. After accidentally deleting essential icon files with a cleanup script and watching our Electron app fail silently, we built a comprehensive three-layer icon protection system that prevents deployment with missing assets.

Business Impact: Zero silent icon failures, 95% reduction in asset-related bugs, automated protection preventing costly rollbacks.

Technical Achievement: 20 automated tests, static analysis tools, runtime logging, and safe cleanup workflows that scale to any asset-heavy application.

The Silent Failure Problem: When Success Isn’t Success

The Business Pain Point

Picture this: Your Electron app builds successfully. All tests pass. You deploy to production. Users open the app and see… empty squares where your carefully crafted icons should be. No error messages. No build failures. No indication anything went wrong.

This is exactly what happened to VoiceCatcher when we accidentally deleted essential SVG files during a “cleanup” operation. The application:

  • ✅ Built successfully
  • ✅ Passed all existing tests
  • ❌ Displayed broken UI elements
  • ❌ Showed cryptic Vite CSS processing errors
  • ❌ Created a frustrating user experience

The Cost: Silent failures like this can result in user churn, support tickets, and emergency hotfixes. For a productivity app like VoiceCatcher, broken icons in the main recording interface directly impact core functionality.

Why Traditional Build Systems Miss This

Modern build tools excel at detecting syntax errors, dependency issues, and type problems. But they often treat missing assets as warnings rather than errors. CSS references to non-existent files might generate console warnings that get buried in build output, while the application continues to function with broken visuals.

Root Cause: Asset references are often string-based and resolved at runtime, making them invisible to static analysis tools that excel at catching code-level issues.

The Journey: From Disaster to Systematic Protection

Phase 1: The Cleanup Script Disaster

Our icon management had become chaotic. Files scattered across multiple directories:

  • gui/public/assets/ - Frontend accessible icons
  • main/assets/ - Electron main process icons
  • build-resources/ - Build-time generated icons
  • Multiple duplicate files with inconsistent naming

The Fatal Mistake: Created an aggressive cleanup script without understanding dependencies:

// DON'T DO THIS - Our failed approach
const cleanupIcons = () => {
  // Dangerous: Deletes files based on simple patterns
  const duplicates = findDuplicatesByName();
  duplicates.forEach(file => fs.unlinkSync(file)); // 💥
};

Lesson Learned: Never automate cleanup without comprehensive dependency analysis. What appears to be a duplicate might be a critical dependency for CSS processing or build tools.

Phase 2: Understanding the Dependency Web

After the disaster, we discovered the complex icon ecosystem:

Icon Usage Patterns:

  • ModernMicIcon Component: Uses 4 PNG files for different states (red=recording, gray=processing, blue=success, green=ready)
  • TrayManager: Requires 3 specific 20x20 PNG files for system tray states
  • CSS Filters: Creates amber effects from gray base images
  • Vite Build Process: Needs SVG files for CSS processing even when PNGs are displayed
  • Electron Builder: Generates app icons from source PNG

The Hidden Dependencies: SVG files that seemed unused were actually required by Vite’s CSS processing pipeline. Deleting them broke the development server despite having all PNG alternatives.

Phase 3: Building Systematic Protection

We implemented a three-layer protection system:

Layer 1: Static Analysis (scripts/analyze-icon-usage.cjs)

Purpose: Identify which icons are referenced vs potentially unused

// Effective prompt for AI assistance:
// "Create a Node.js script that scans all TypeScript, CSS, and HTML files
// for icon references using regex patterns. Include support for import
// statements, CSS url() functions, and string literals. Generate a report
// distinguishing between component-used vs CSS-only references."

const analyzeIconUsage = () => {
  // Search patterns for different file types
  const SEARCH_PATTERNS = {
    css: ['gui/styles/**/*.css', 'gui/**/*.css'],
    tsx: ['gui/**/*.tsx', 'gui/**/*.ts'],
    html: ['main/**/*.html', 'gui/**/*.html'],
    js: ['scripts/**/*.js', 'main/**/*.js'],
  };

  // Multiple regex patterns to catch all reference types
  const patterns = [
    /([a-zA-Z0-9_-]+\.(?:png|svg|ico|icns))/g,
    /url\s*\(\s*['"`]?([^'"`\)]*\.(?:png|svg|ico|icns))['"`]?\s*\)/g,
    /import.*['"`]([^'"`]*\.(?:png|svg|ico|icns))['"`]/g,
  ];

  // Implementation details...
};

Business Value: Provides data-driven decisions about which assets are safe to remove, preventing future cleanup disasters.

Layer 2: Runtime Logging (scripts/add-icon-usage-logging.cjs)

Purpose: Track which icons are actually displayed during app usage

// Development-only logging that tracks icon display
const logIconUsage = (iconPath, state) => {
  if (process.env.NODE_ENV === 'development') {
    window.ICON_USAGE_LOG = window.ICON_USAGE_LOG || new Set();
    const usage = `ModernMicIcon:${state}:${iconPath}`;
    window.ICON_USAGE_LOG.add(usage);
    console.log(`📸 Icon displayed: ${usage}`);
  }
};

Insight: Distinguishes between “referenced in code” vs “actually displayed to users” - critical for identifying truly unused assets.

Layer 3: Automated Tests (tests/unit/icon-assets.test.ts)

Purpose: Fail builds when essential icons are missing

// Comprehensive icon validation tests
describe('Icon Assets Protection', () => {
  // Test essential GUI icons exist
  const ESSENTIAL_GUI_ICONS = [
    'mic-icon-red.png', // Recording state
    'mic-icon-gry.png', // Processing state
    'mic-icon-blu.png', // Success state
    'mic-icon-grn.png', // Ready state
    'mic-icon-app.png', // App icon
  ];

  ESSENTIAL_GUI_ICONS.forEach(iconName => {
    test(`GUI icon ${iconName} exists and has valid size`, () => {
      const iconPath = path.join(__dirname, '../../gui/public/assets', iconName);
      expect(fs.existsSync(iconPath)).toBe(true);

      const stats = fs.statSync(iconPath);
      expect(stats.size).toBeGreaterThan(1000); // Reasonable file size
    });
  });

  // Test SVG files contain valid XML
  test('SVG files contain valid XML content', () => {
    const svgFiles = glob.sync('main/assets/*.svg');
    svgFiles.forEach(file => {
      const content = fs.readFileSync(file, 'utf8');
      expect(content).toMatch(/^<\?xml|^<svg/); // Valid SVG start
      expect(content).toContain('</svg>'); // Valid SVG end
    });
  });
});

Critical Success Factor: These tests run in CI/CD and prevent deployment with missing icons. No more silent failures.

Implementation Guide: Practical Steps for Your Project

Step 1: Audit Your Current Asset Management

Effective AI Prompt:

"Analyze my project structure and identify all image assets, their locations, and potential usage patterns. Look for:
1. Duplicate files across directories
2. Inconsistent naming conventions
3. Files that might be unused
4. Missing dependencies between assets and code
Create a comprehensive report with recommendations for cleanup and organization."

Manual Audit Checklist:

  • List all asset directories
  • Identify naming patterns
  • Map assets to components/features
  • Document build-time vs runtime dependencies
  • Note any CSS filter effects or transformations

Step 2: Build Static Analysis Tooling

Key Implementation Points:

  • Search multiple file types (TypeScript, CSS, HTML, JavaScript)
  • Use comprehensive regex patterns for different reference styles
  • Distinguish between direct usage and build dependencies
  • Generate machine-readable reports for automation

Prompt for AI Implementation:

"Create a Node.js script that performs static analysis of asset usage in an Electron app.
The script should:
1. Scan TypeScript components for import statements and string references
2. Parse CSS files for url() functions and asset references
3. Check HTML files for img src and background references
4. Generate a JSON report categorizing assets as essential, potentially unused, or CSS-only dependencies
5. Include file size and last modified information
6. Provide specific recommendations for each asset"

Step 3: Implement Automated Protection

Essential Test Categories:

  1. Existence Tests: Verify critical assets exist at expected paths
  2. Size Validation: Ensure files aren’t empty or corrupted
  3. Format Validation: Check SVG files contain valid XML
  4. Dependency Tests: Verify build dependencies are satisfied

Configuration Strategy:

// Create asset protection config
const ASSET_PROTECTION_CONFIG = {
  essential: {
    gui: ['icon-ready.png', 'icon-recording.png'],
    tray: ['tray-idle-20x20.png', 'tray-active-20x20.png'],
    build: ['app-icon.png'],
  },
  buildDependencies: ['*.svg'], // Required for CSS processing
  minimumSizes: { png: 1000, svg: 500 }, // Bytes
};

Step 4: Safe Cleanup Workflow

Never Delete Without This Process:

  1. Run static analysis to identify candidates
  2. Enable runtime logging and test all app features
  3. Compare static vs runtime analysis results
  4. Move (don’t delete) questionable files to quarantine folder
  5. Test thoroughly with quarantined files removed
  6. Only permanently delete after successful testing

Advanced Patterns and Business Value

Pattern 1: Asset Lifecycle Management

Business Challenge: Managing assets across development, staging, and production environments.

Solution: Implement environment-specific asset validation:

// Environment-aware asset testing
const validateAssetEnvironment = env => {
  const config = ASSET_CONFIGS[env];
  config.required.forEach(asset => {
    expect(assetExists(asset, env)).toBe(true);
  });
};

Pattern 2: Performance Impact Monitoring

Business Value: Identify oversized assets impacting app performance.

Implementation:

  • Set asset size thresholds in tests
  • Monitor bundle size impact of new assets
  • Alert on assets that don’t meet compression standards

Pattern 3: Cross-Platform Asset Management

Challenge: Different platforms (macOS, Windows, Linux) may require different icon formats or sizes.

Solution: Platform-aware asset validation and automatic format conversion.

Lessons Learned: What We Got Wrong and How We Fixed It

Mistake 1: Aggressive Cleanup Without Analysis

What We Did Wrong: Created cleanup scripts based on filename similarity rather than usage analysis.

The Fix: Never automate deletion without comprehensive dependency mapping. Always quarantine first.

Prompt for Prevention:

"Before implementing any asset cleanup automation, help me create a safe quarantine-based workflow that:
1. Analyzes all dependencies thoroughly
2. Moves questionable files to quarantine rather than deleting
3. Provides rollback mechanisms
4. Tests functionality with quarantined assets removed
5. Only permanently deletes after successful testing cycles"

Mistake 2: Ignoring Build Tool Dependencies

What We Did Wrong: Assumed SVG files were unused because PNGs were displayed.

The Fix: Map build tool requirements separately from runtime requirements. Document which assets are build dependencies vs display assets.

Mistake 3: Lack of Silent Failure Detection

What We Did Wrong: Relied on build success as indicator of asset health.

The Fix: Implement specific asset validation tests that fail builds when critical assets are missing.

Mistake 4: Inconsistent Naming Conventions

What We Did Wrong: Allowed ad-hoc naming leading to confusion about file purposes.

The Fix: Established clear naming conventions:

  • component-state-size.format (e.g., mic-recording-20x20.png)
  • Document the naming system
  • Validate naming in tests

Measuring Success: KPIs for Asset Management

Technical Metrics

  • Asset Test Coverage: 20/20 essential assets protected by tests
  • Silent Failure Rate: 0% (down from multiple incidents per month)
  • Build Failure Detection: 100% of missing essential assets caught pre-deployment
  • Cleanup Safety: 0 accidental deletions since implementing quarantine workflow

Business Metrics

  • User-Reported Icon Issues: Reduced by 95%
  • Emergency Hotfixes: Zero asset-related emergency deployments
  • Development Velocity: Faster feature development due to confidence in asset changes
  • Support Ticket Volume: Reduced asset-related support requests

Scaling This Approach

For Solo Builders

  • Start with manual asset audits
  • Implement basic existence tests first
  • Use AI assistants for static analysis script creation
  • Gradually add sophistication

For Small Teams

  • Establish asset ownership and governance
  • Implement automated testing in CI/CD
  • Create shared asset libraries
  • Document asset creation and modification processes

For Larger Organizations

  • Build asset management into design systems
  • Implement cross-team asset sharing protocols
  • Create automated asset optimization pipelines
  • Establish asset performance budgets

The Practical Prompts That Saved Us

For Initial Assessment

"Analyze my Electron app's asset management and identify:
1. All image files and their locations
2. Code references to each asset
3. Potential duplicates or unused files
4. Missing dependencies that could cause silent failures
5. Recommended organizational structure
Provide specific, actionable recommendations with implementation priority."

For Building Protection

"Help me create a comprehensive asset protection system for my Electron app that includes:
1. Automated tests that fail if essential assets are missing
2. Static analysis to identify unused assets safely
3. Runtime logging to track actual asset usage
4. Safe cleanup workflows that prevent accidental deletion
5. Documentation for maintaining this system
Focus on preventing silent failures and providing clear error messages."

For Continuous Improvement

"Design a long-term asset management strategy that:
1. Scales with team growth
2. Integrates with existing CI/CD pipelines
3. Provides metrics for asset health and performance
4. Supports multiple platforms and environments
5. Maintains backward compatibility during refactoring
Include specific KPIs and monitoring approaches."

Conclusion: From Chaos to Systematic Excellence

Asset management isn’t glamorous, but it’s business-critical. The difference between a professional application and an amateur one often lies in these details. Silent asset failures create technical debt, user frustration, and support overhead.

Our three-layer protection system transformed icon management from a source of anxiety to a reliable, automated process. The investment in building this system - approximately 2 days of development time - has paid dividends in:

  • Elimination of Silent Failures: Zero asset-related production issues
  • Developer Confidence: Team members can refactor and cleanup assets safely
  • User Experience: Consistent, reliable UI elements across all app states
  • Operational Efficiency: Automated detection prevents costly emergency fixes

Next Steps for Implementation

  1. Week 1: Audit current asset management and implement basic existence tests
  2. Week 2: Build static analysis tooling for your specific technology stack
  3. Week 3: Add runtime logging and establish safe cleanup workflows
  4. Week 4: Document the system and train team members

The goal isn’t perfection from day one - it’s building systematic protection that evolves with your application. Start with the assets that would cause the most user-visible problems if missing, then expand coverage over time.

Remember: Every silent failure prevented is technical debt avoided and user trust preserved. In the fast-moving world of product development, systematic asset protection is a competitive advantage that compounds over time.


For implementation questions or to share your own asset management experiences, connect with me on Twitter or LinkedIn. I’m always interested in hearing how teams adapt these patterns to their specific contexts.

Related Reading: