Skip to content

performance-check

Access: /template performance-check or /t performance-check

Analyzes your MakerKit application's performance, identifying bottlenecks and providing optimization recommendations.

What It Does

The performance-check command helps you:

  • Analyze bundle sizes and code splitting
  • Check Core Web Vitals compliance
  • Identify slow database queries
  • Review caching effectiveness
  • Detect memory leaks
  • Analyze API response times
  • Generate performance reports

Usage

bash
/template performance-check "Description"
# or
/t performance-check "Description"

When prompted, specify:

  • Check type (quick scan or deep analysis)
  • Performance targets
  • Areas of concern
  • Report format

Prerequisites

  • A running MakerKit application
  • Basic performance baseline
  • Commercial MakerKit license from MakerKit

What Gets Analyzed

1. Bundle Size Analysis

typescript
// Bundle analyzer
async function analyzeBundleSize() {
  // Run Next.js bundle analyzer
  await execAsync('ANALYZE=true pnpm build');
  
  const stats = await readBuildStats();
  const issues = [];
  
  // Check total bundle size
  if (stats.totalSize > 500 * 1024) { // 500KB
    issues.push({
      severity: 'high',
      metric: 'Total Bundle Size',
      current: formatBytes(stats.totalSize),
      target: '< 500KB',
      impact: 'Slower initial page load',
    });
  }
  
  // Check largest chunks
  const largeChunks = stats.chunks
    .filter(chunk => chunk.size > 100 * 1024) // 100KB
    .sort((a, b) => b.size - a.size);
  
  for (const chunk of largeChunks) {
    issues.push({
      severity: 'medium',
      metric: `Chunk: ${chunk.name}`,
      current: formatBytes(chunk.size),
      target: '< 100KB',
      recommendation: `Consider code splitting ${chunk.name}`,
    });
  }
  
  // Check for duplicate modules
  const duplicates = findDuplicateModules(stats);
  if (duplicates.length > 0) {
    issues.push({
      severity: 'medium',
      metric: 'Duplicate Modules',
      current: duplicates.length,
      modules: duplicates,
      recommendation: 'Deduplicate shared dependencies',
    });
  }
  
  return issues;
}

// Tree shaking effectiveness
async function checkTreeShaking() {
  const unusedExports = await findUnusedExports();
  
  if (unusedExports.length > 0) {
    return {
      severity: 'low',
      metric: 'Unused Exports',
      count: unusedExports.length,
      files: unusedExports.slice(0, 10),
      recommendation: 'Remove unused exports to reduce bundle size',
    };
  }
  
  return null;
}

2. Core Web Vitals

typescript
// Lighthouse CI integration
async function measureCoreWebVitals() {
  const urls = [
    '/',
    '/dashboard',
    '/billing',
    '/settings',
  ];
  
  const results = {};
  
  for (const url of urls) {
    const { stdout } = await execAsync(
      `lighthouse ${process.env.NEXT_PUBLIC_APP_URL}${url} --output=json --quiet`
    );
    
    const report = JSON.parse(stdout);
    const metrics = report.audits;
    
    results[url] = {
      lcp: metrics['largest-contentful-paint'],
      fid: metrics['max-potential-fid'],
      cls: metrics['cumulative-layout-shift'],
      fcp: metrics['first-contentful-paint'],
      ttfb: metrics['server-response-time'],
      tti: metrics['interactive'],
      tbt: metrics['total-blocking-time'],
      score: report.categories.performance.score * 100,
    };
  }
  
  return analyzeCWVResults(results);
}

function analyzeCWVResults(results) {
  const issues = [];
  const thresholds = {
    lcp: 2500, // 2.5s
    fid: 100,  // 100ms
    cls: 0.1,  // 0.1
    fcp: 1800, // 1.8s
    tti: 3800, // 3.8s
  };
  
  for (const [url, metrics] of Object.entries(results)) {
    for (const [metric, data] of Object.entries(metrics)) {
      if (thresholds[metric] && data.numericValue > thresholds[metric]) {
        issues.push({
          url,
          metric: metric.toUpperCase(),
          current: data.displayValue,
          target: `< ${thresholds[metric]}`,
          severity: data.score < 0.5 ? 'high' : 'medium',
        });
      }
    }
  }
  
  return issues;
}

3. Database Performance

typescript
// Query performance analysis
async function analyzeQueryPerformance() {
  const client = getSupabaseServerClient({ admin: true });
  
  // Get slow queries
  const { data: slowQueries } = await client.rpc('get_slow_queries', {
    threshold_ms: 100,
    limit: 20,
  });
  
  const issues = [];
  
  for (const query of slowQueries) {
    issues.push({
      severity: query.mean_time > 500 ? 'high' : 'medium',
      query: query.query.substring(0, 100) + '...',
      avgTime: `${query.mean_time}ms`,
      calls: query.calls,
      recommendation: await getQueryOptimization(query),
    });
  }
  
  // Check for missing indexes
  const { data: missingIndexes } = await client.rpc('suggest_indexes');
  
  for (const suggestion of missingIndexes) {
    issues.push({
      severity: 'medium',
      table: suggestion.table_name,
      columns: suggestion.columns,
      reason: suggestion.reason,
      sql: suggestion.create_index_sql,
    });
  }
  
  // Check table sizes
  const { data: tableSizes } = await client.rpc('get_table_sizes');
  
  for (const table of tableSizes) {
    if (table.total_size > 1024 * 1024 * 1024) { // 1GB
      issues.push({
        severity: 'low',
        table: table.table_name,
        size: formatBytes(table.total_size),
        rows: table.row_count,
        recommendation: 'Consider archiving old data or partitioning',
      });
    }
  }
  
  return issues;
}

// N+1 query detection
async function detectNPlusOneQueries() {
  // Monitor queries during test scenarios
  const scenarios = [
    { name: 'Load Dashboard', fn: loadDashboard },
    { name: 'List Projects', fn: listProjects },
    { name: 'View Team Members', fn: viewTeamMembers },
  ];
  
  const issues = [];
  
  for (const scenario of scenarios) {
    const queries = await monitorQueries(scenario.fn);
    
    // Detect repeated similar queries
    const patterns = groupSimilarQueries(queries);
    
    for (const [pattern, instances] of patterns) {
      if (instances.length > 5) {
        issues.push({
          severity: 'high',
          scenario: scenario.name,
          pattern,
          count: instances.length,
          recommendation: 'Use JOIN or batch query instead of multiple queries',
        });
      }
    }
  }
  
  return issues;
}

4. API Performance

typescript
// API endpoint analysis
async function analyzeAPIPerformance() {
  const endpoints = await getAPIEndpoints();
  const issues = [];
  
  for (const endpoint of endpoints) {
    // Measure response time
    const times = [];
    for (let i = 0; i < 10; i++) {
      const start = Date.now();
      await fetch(endpoint.url, endpoint.options);
      times.push(Date.now() - start);
    }
    
    const avgTime = times.reduce((a, b) => a + b) / times.length;
    const p95Time = times.sort((a, b) => a - b)[Math.floor(times.length * 0.95)];
    
    if (avgTime > 200) {
      issues.push({
        severity: avgTime > 1000 ? 'high' : 'medium',
        endpoint: endpoint.path,
        avgResponseTime: `${avgTime}ms`,
        p95ResponseTime: `${p95Time}ms`,
        recommendation: await getAPIOptimization(endpoint),
      });
    }
  }
  
  return issues;
}

// Cache effectiveness
async function analyzeCaching() {
  const cacheStats = await getCacheStatistics();
  const issues = [];
  
  // Check cache hit rate
  if (cacheStats.hitRate < 0.8) {
    issues.push({
      severity: 'medium',
      metric: 'Cache Hit Rate',
      current: `${(cacheStats.hitRate * 100).toFixed(1)}%`,
      target: '> 80%',
      recommendation: 'Review cache keys and TTL settings',
    });
  }
  
  // Check cache size
  if (cacheStats.size > cacheStats.maxSize * 0.9) {
    issues.push({
      severity: 'high',
      metric: 'Cache Usage',
      current: formatBytes(cacheStats.size),
      max: formatBytes(cacheStats.maxSize),
      recommendation: 'Increase cache size or implement eviction policy',
    });
  }
  
  // Check stale data
  const staleEntries = await findStaleCacheEntries();
  if (staleEntries.length > 0) {
    issues.push({
      severity: 'low',
      metric: 'Stale Cache Entries',
      count: staleEntries.length,
      recommendation: 'Implement cache invalidation on data updates',
    });
  }
  
  return issues;
}

5. Memory Usage

typescript
// Memory leak detection
async function checkMemoryUsage() {
  const issues = [];
  
  // Check Node.js memory
  const memUsage = process.memoryUsage();
  const heapUsedMB = memUsage.heapUsed / 1024 / 1024;
  
  if (heapUsedMB > 500) {
    issues.push({
      severity: 'high',
      metric: 'Heap Memory Usage',
      current: `${heapUsedMB.toFixed(2)}MB`,
      target: '< 500MB',
      details: {
        rss: formatBytes(memUsage.rss),
        heapTotal: formatBytes(memUsage.heapTotal),
        heapUsed: formatBytes(memUsage.heapUsed),
        external: formatBytes(memUsage.external),
      },
    });
  }
  
  // Check for memory leaks
  const leaks = await detectMemoryLeaks();
  if (leaks.length > 0) {
    issues.push({
      severity: 'critical',
      metric: 'Memory Leaks Detected',
      count: leaks.length,
      sources: leaks,
      recommendation: 'Fix memory leaks to prevent crashes',
    });
  }
  
  return issues;
}

// Component re-render analysis
async function analyzeReactPerformance() {
  // Use React DevTools Profiler API
  const profilerData = await collectProfilerData();
  const issues = [];
  
  // Find components with excessive re-renders
  const problematicComponents = profilerData.components
    .filter(c => c.renderCount > 10 && c.renderTime > 16)
    .sort((a, b) => b.renderTime - a.renderTime);
  
  for (const component of problematicComponents) {
    issues.push({
      severity: component.renderTime > 50 ? 'high' : 'medium',
      component: component.name,
      renderCount: component.renderCount,
      avgRenderTime: `${component.renderTime.toFixed(2)}ms`,
      recommendation: 'Use React.memo() or useMemo() to prevent unnecessary re-renders',
    });
  }
  
  return issues;
}

6. Image Optimization

typescript
// Image performance analysis
async function analyzeImagePerformance() {
  const images = await scanProjectImages();
  const issues = [];
  
  for (const image of images) {
    // Check image size
    if (image.size > 200 * 1024) { // 200KB
      issues.push({
        severity: 'medium',
        file: image.path,
        currentSize: formatBytes(image.size),
        recommendation: 'Compress image or use next-gen format (WebP/AVIF)',
      });
    }
    
    // Check if using next/image
    const usesNextImage = await checkNextImageUsage(image.path);
    if (!usesNextImage) {
      issues.push({
        severity: 'low',
        file: image.path,
        issue: 'Not using next/image component',
        recommendation: 'Use next/image for automatic optimization',
      });
    }
    
    // Check dimensions
    if (image.width > 2000 || image.height > 2000) {
      issues.push({
        severity: 'low',
        file: image.path,
        dimensions: `${image.width}x${image.height}`,
        recommendation: 'Resize image to appropriate dimensions',
      });
    }
  }
  
  return issues;
}

7. Performance Report

typescript
// Generate comprehensive performance report
export async function generatePerformanceReport() {
  console.log('🚀 Starting Performance Analysis...\n');
  
  const report = {
    timestamp: new Date().toISOString(),
    summary: {
      score: 0,
      critical: 0,
      high: 0,
      medium: 0,
      low: 0,
    },
    categories: {},
    recommendations: [],
  };
  
  // Run all checks
  const checks = [
    { name: 'Bundle Size', fn: analyzeBundleSize, weight: 0.2 },
    { name: 'Core Web Vitals', fn: measureCoreWebVitals, weight: 0.3 },
    { name: 'Database', fn: analyzeQueryPerformance, weight: 0.2 },
    { name: 'API Performance', fn: analyzeAPIPerformance, weight: 0.15 },
    { name: 'Memory Usage', fn: checkMemoryUsage, weight: 0.1 },
    { name: 'Images', fn: analyzeImagePerformance, weight: 0.05 },
  ];
  
  let totalScore = 0;
  
  for (const check of checks) {
    console.log(`Analyzing ${check.name}...`);
    const issues = await check.fn();
    report.categories[check.name] = issues;
    
    // Calculate score for this category
    const categoryScore = calculateCategoryScore(issues);
    totalScore += categoryScore * check.weight;
    
    // Update issue counts
    for (const issue of issues) {
      report.summary[issue.severity]++;
    }
  }
  
  report.summary.score = Math.round(totalScore);
  
  // Generate recommendations
  report.recommendations = generateRecommendations(report);
  
  // Create visual report
  const html = generatePerformanceHTML(report);
  await fs.writeFile('performance-report.html', html);
  
  // Create JSON report
  await fs.writeFile('performance-report.json', JSON.stringify(report, null, 2));
  
  console.log('\n📊 Performance Analysis Complete!');
  console.log(`Overall Score: ${report.summary.score}/100`);
  console.log(`Critical Issues: ${report.summary.critical}`);
  console.log(`High Priority: ${report.summary.high}`);
  console.log(`Medium Priority: ${report.summary.medium}`);
  console.log(`Low Priority: ${report.summary.low}`);
  
  return report;
}

function generateRecommendations(report) {
  const recommendations = [];
  
  // Priority 1: Critical issues
  if (report.summary.critical > 0) {
    recommendations.push({
      priority: 1,
      title: 'Fix Critical Performance Issues',
      description: 'Address memory leaks and blocking issues immediately',
      impact: 'Prevents crashes and improves stability',
    });
  }
  
  // Priority 2: Core Web Vitals
  const cwv = report.categories['Core Web Vitals'];
  if (cwv && cwv.some(i => i.severity === 'high')) {
    recommendations.push({
      priority: 2,
      title: 'Improve Core Web Vitals',
      description: 'Optimize LCP, FID, and CLS for better user experience',
      impact: 'Better SEO and user engagement',
    });
  }
  
  // Priority 3: Bundle size
  const bundle = report.categories['Bundle Size'];
  if (bundle && bundle.some(i => i.severity === 'high')) {
    recommendations.push({
      priority: 3,
      title: 'Reduce JavaScript Bundle Size',
      description: 'Implement code splitting and tree shaking',
      impact: 'Faster initial page loads',
    });
  }
  
  return recommendations;
}

Automated Optimizations

The command can apply some optimizations automatically:

typescript
// Auto-optimize
export async function applyPerformanceOptimizations() {
  console.log('⚡ Applying automatic optimizations...\n');
  
  // Optimize images
  await optimizeImages();
  
  // Update Next.js config
  await updateNextConfig();
  
  // Add performance monitoring
  await setupPerformanceMonitoring();
  
  // Generate optimized builds
  await execAsync('pnpm build');
  
  console.log('✅ Optimizations applied');
}

License Requirement

Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.

Built with ❤️ for the AI Coding community, by Praney Behl