Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Remove comments and debug code in production

Unnecessary code, comments, and debug elements are removed before deploying to production.

Utilities
Quick take
Typical fix time 10 min
  • Remove TODO, FIXME, DEBUG comments before production
  • Keep accessibility and legal comments
  • Configure build tools to strip comments automatically
  • Review for sensitive data leaks in comments
Why it matters: Comments expose internal logic to attackers, increase file size, and can leak sensitive information like API endpoints, credentials, or system architecture.

Rule Details

Remove unnecessary code, comments, and debug elements before deploying to production to reduce file size and improve security.

Code Examples

Development Comments

<!-- ❌ Remove: Development comments -->
<!-- TODO: Fix this later -->
<!-- FIXME: This is a temporary solution -->
<!-- DEBUG: Testing responsive layout -->
<!-- This section needs refactoring -->
 
<!-- ✅ Keep: Important comments for production -->
<!-- Skip to main content link for accessibility -->
<a href="#main" class="skip-link">Skip to main content</a>
 
<!-- Critical above-the-fold styles -->
<style>
  /* Critical CSS for initial page load */
</style>

Debug Code and Testing Elements

<!-- ❌ Remove: Debug elements -->
<div class="debug-info" style="background: red;">
  Debug: Current viewport width
</div>
 
<div id="test-element" data-test="debugging">
  Test content for development
</div>
 
<!-- ❌ Remove: Console logs in inline scripts -->
<script>
  console.log('Page loaded for debugging')
  console.table(userData)
  debugger; // Remove debugger statements
</script>
 
<!-- ✅ Clean production version -->
<script>
  // Only production-ready code
  initializeApp()
</script>

Unused Markup and Dead Code

<!-- ❌ Remove: Commented out old code -->
<!--
<div class="old-layout">
  <p>This was the old design</p>
</div>
-->
 
<!-- ❌ Remove: Unused elements -->
<div class="feature-not-implemented" style="display: none;">
  Future feature placeholder
</div>
 
<!-- ❌ Remove: Empty or meaningless elements -->
<div></div>
<span class="unused"></span>
<p>&nbsp;</p>

Why It Matters

Comments expose internal logic to attackers, increase file size, and can leak sensitive information like API endpoints, credentials, or system architecture.

Framework Examples

Next.js Production Build

// ❌ Development version
export default function HomePage() {
  // TODO: Implement user preferences
  const debugMode = true // Remove debug flags
 
  return (
    <div>
      {/* DEBUG: Check responsive breakpoints */}
      <div className={`container ${debugMode ? 'debug-borders' : ''}`}>
        <h1>Welcome</h1>
 
        {/* FIXME: Temporary hardcoded content */}
        <p>This needs to be dynamic</p>
 
        {debugMode && (
          <div className="debug-panel">
            <p>Debug info here</p>
          </div>
        )}
      </div>
    </div>
  )
}
 
// ✅ Production version
export default function HomePage() {
  return (
    <div>
      <div className="container">
        <h1>Welcome</h1>
        <p>Dynamic content from CMS</p>
      </div>
    </div>
  )
}

React Component Cleanup

// ❌ Development version with debug code
import React, { useState, useEffect } from 'react'
 
function ProductList({ products }) {
  const [debugInfo, setDebugInfo] = useState(null)
 
  useEffect(() => {
    // Debug: Log component mounting
    console.log('ProductList mounted with', products.length, 'products')
 
    // TODO: Add error boundary
    setDebugInfo({
      timestamp: Date.now(),
      productCount: products.length
    })
  }, [products])
 
  return (
    <div className="product-list">
      {/* Development helper */}
      {process.env.NODE_ENV === 'development' && (
        <div className="debug-toolbar">
          <button onClick={() => console.table(products)}>
            Debug Products
          </button>
        </div>
      )}
 
      <h2>Products ({products.length})</h2>
 
      {/* TEMP: Hardcoded featured product */}
      <div className="featured-temp">
        <p>Featured product placeholder</p>
      </div>
 
      {products.map(product => (
        <div key={product.id} className="product-card">
          <h3>{product.name}</h3>
          <p>{product.description}</p>
          {/* TODO: Add to cart functionality */}
        </div>
      ))}
 
      {/* Debug info display */}
      {debugInfo && (
        <pre className="debug-info">
          {JSON.stringify(debugInfo, null, 2)}
        </pre>
      )}
    </div>
  )
}
 
// ✅ Clean production version
import React from 'react'
 
function ProductList({ products }) {
  return (
    <div className="product-list">
      <h2>Products ({products.length})</h2>
 
      {products.map(product => (
        <div key={product.id} className="product-card">
          <h3>{product.name}</h3>
          <p>{product.description}</p>
          <button className="add-to-cart">Add to Cart</button>
        </div>
      ))}
    </div>
  )
}

Vue.js Template Cleanup

<!-- ❌ Development version -->
<template>
  <div class="user-profile">
    <!-- DEBUG: Component state inspection -->
    <div v-if="$store.state.debug" class="debug-panel">
      <h4>Debug Info</h4>
      <pre>{{ $data }}</pre>
    </div>
 
    <h1>{{ user.name }}</h1>
 
    <!-- TODO: Add user preferences section -->
    <!-- <UserPreferences :user="user" /> -->
 
    <!-- TEMP: Mock data display -->
    <div class="temp-content">
      <p>Using temporary mock data</p>
    </div>
 
    <div class="user-details">
      <p>Email: {{ user.email }}</p>
      <!-- FIXME: Format date properly -->
      <p>Joined: {{ user.createdAt }}</p>
    </div>
  </div>
</template>
 
<script>
export default {
  name: 'UserProfile',
  props: ['user'],
  mounted() {
    // Debug logging
    console.log('UserProfile mounted:', this.user)
 
    // TODO: Track user profile views
    // this.trackPageView()
  }
}
</script>
 
<!-- ✅ Clean production version -->
<template>
  <div class="user-profile">
    <h1>{{ user.name }}</h1>
 
    <UserPreferences :user="user" />
 
    <div class="user-details">
      <p>Email: {{ user.email }}</p>
      <p>Joined: {{ formatDate(user.createdAt) }}</p>
    </div>
  </div>
</template>
 
<script>
export default {
  name: 'UserProfile',
  props: ['user'],
  methods: {
    formatDate(date) {
      return new Date(date).toLocaleDateString()
    }
  },
  mounted() {
    this.trackPageView()
  }
}
</script>

Build Tool Integration

Webpack Production Cleanup

// webpack.config.js
const webpack = require('webpack')
 
module.exports = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
 
  plugins: [
    // Remove comments in production
    new webpack.BannerPlugin({
      banner: '/*! Built for production */',
      test: /\.js$/
    }),
 
    // Remove console logs in production
    ...(process.env.NODE_ENV === 'production' ? [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify('production')
      })
    ] : []),
  ],
 
  optimization: {
    minimize: process.env.NODE_ENV === 'production',
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true, // Remove console.* calls
            drop_debugger: true, // Remove debugger statements
            pure_funcs: ['console.log', 'console.info'], // Remove specific functions
          },
          mangle: true,
          output: {
            comments: false, // Remove comments
          },
        },
      }),
    ],
  },
}

Vite Production Configuration

// vite.config.js
import { defineConfig } from 'vite'
 
export default defineConfig({
  build: {
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
      format: {
        comments: false,
      },
    },
 
    // Remove unused CSS
    rollupOptions: {
      treeshake: true,
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash', 'axios']
        }
      }
    }
  },
 
  // Environment-specific settings
  define: {
    __DEV__: process.env.NODE_ENV !== 'production',
  }
})

PostCSS Comment Removal

// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer'),
 
    // Remove comments in production
    ...(process.env.NODE_ENV === 'production' ? [
      require('postcss-discard-comments')({
        removeAll: true // Remove all comments
      })
    ] : []),
 
    require('cssnano')({
      preset: 'default'
    })
  ]
}

Automated Cleanup Scripts

Pre-deployment Script

#!/bin/bash
# scripts/cleanup-for-production.sh
 
echo "🧹 Cleaning up for production deployment..."
 
# Remove debug files
find . -name "*.debug.js" -delete
find . -name "*.test.js" -delete
find . -name "*.spec.js" -delete
 
# Remove development-only directories
rm -rf .storybook/
rm -rf __tests__/
rm -rf coverage/
 
# Remove unnecessary files
rm -f .env.local
rm -f .env.development
rm -f webpack.dev.js
 
# Clean up package.json scripts (remove dev scripts)
node scripts/cleanup-package-json.js
 
echo "✅ Production cleanup complete!"

Node.js Cleanup Script

// scripts/remove-debug-code.js
const fs = require('fs')
const path = require('path')
 
function removeDebugCode(filePath) {
  let content = fs.readFileSync(filePath, 'utf8')
 
  // Remove console.log statements
  content = content.replace(/console\.log\([^)]*\);?\s*/g, '')
 
  // Remove debugger statements
  content = content.replace(/debugger;?\s*/g, '')
 
  // Remove TODO/FIXME comments
  content = content.replace(/\/\*[\s]*TODO:.*?\*\//gs, '')
  content = content.replace(/\/\/[\s]*TODO:.*$/gm, '')
  content = content.replace(/\/\*[\s]*FIXME:.*?\*\//gs, '')
  content = content.replace(/\/\/[\s]*FIXME:.*$/gm, '')
 
  // Remove debug blocks
  content = content.replace(/\/\*[\s]*DEBUG[\s\S]*?END DEBUG[\s]*\*\//g, '')
 
  fs.writeFileSync(filePath, content)
}
 
// Process all JavaScript files
function processDirectory(dir) {
  const files = fs.readdirSync(dir)
 
  files.forEach(file => {
    const filePath = path.join(dir, file)
    const stat = fs.statSync(filePath)
 
    if (stat.isDirectory() && !file.startsWith('.') && file !== 'node_modules') {
      processDirectory(filePath)
    } else if (file.endsWith('.js') || file.endsWith('.jsx') || file.endsWith('.ts') || file.endsWith('.tsx')) {
      removeDebugCode(filePath)
    }
  })
}
 
processDirectory('./src')
console.log('✅ Debug code removed from all source files')

HTML Template Cleanup

// scripts/clean-html-templates.js
const fs = require('fs')
const glob = require('glob')
const { JSDOM } = require('jsdom')
 
glob('**/*.html', (err, files) => {
  if (err) throw err
 
  files.forEach(file => {
    const html = fs.readFileSync(file, 'utf8')
    const dom = new JSDOM(html)
    const document = dom.window.document
 
    // Remove elements with debug classes
    const debugElements = document.querySelectorAll('.debug, .temp, [class*="debug"], [class*="temp"]')
    debugElements.forEach(el => el.remove())
 
    // Remove comment nodes
    const walker = document.createTreeWalker(
      document.body,
      dom.window.NodeFilter.SHOW_COMMENT
    )
 
    const commentsToRemove = []
    let node
 
    while (node = walker.nextNode()) {
      if (node.nodeValue.includes('TODO') ||
          node.nodeValue.includes('FIXME') ||
          node.nodeValue.includes('DEBUG')) {
        commentsToRemove.push(node)
      }
    }
 
    commentsToRemove.forEach(comment => comment.remove())
 
    // Save cleaned HTML
    fs.writeFileSync(file, dom.serialize())
  })
 
  console.log(`✅ Cleaned ${files.length} HTML files`)
})

Security Benefits

Remove Sensitive Information

<!-- ❌ Don't leave sensitive info in production -->
<!-- Database connection: mongodb://user:password@localhost/myapp -->
<!-- API Key: sk-1234567890abcdef -->
<!-- Admin panel: /secret-admin-panel -->
 
<!-- ❌ Remove development credentials -->
<script>
  const API_KEY = 'dev-key-12345' // Remove dev keys
  const DEBUG_MODE = true // Remove debug flags
</script>
 
<!-- ✅ Use environment variables in production -->
<script>
  const API_KEY = process.env.NEXT_PUBLIC_API_KEY
</script>

Remove Development Endpoints

// ❌ Remove debug routes
app.get('/debug', (req, res) => {
  res.json({
    environment: process.env,
    database: dbConnection.config,
    users: allUsers // Don't expose user data
  })
})
 
// ❌ Remove test endpoints
app.post('/test-payment', (req, res) => {
  // Always processes without charging
  res.json({ success: true })
})

Performance Benefits

  • Reduced File Size: Remove unnecessary bytes
  • Faster Parsing: Less code to parse and execute
  • Better Caching: Clean code caches more efficiently
  • Improved SEO: Cleaner HTML structure

Tools and Automation

  • Webpack/Vite: Automatic minification and dead code elimination
  • Terser: Remove console logs and debug code
  • PostCSS: Clean CSS comments and unused styles
  • ESLint: Detect console.log and debugger statements
  • Prettier: Consistent code formatting
  • Pre-commit Hooks: Prevent debug code from being committed

Best Practices

  1. Use Environment Variables: Different configs for dev/prod
  2. Automate Cleanup: Build process handles code cleaning
  3. Code Reviews: Check for leftover debug code
  4. Linting Rules: Prevent console.log in production code
  5. Source Maps: Keep debugging capability without exposing source
  6. Feature Flags: Control features without code comments
  7. Staging Environment: Final check before production

Standards

  • Use MDN: HTML as the standard for the final rendered HTML and browser-facing behavior.
  • Use WHATWG HTML Living Standard as the standard for the final rendered HTML and browser-facing behavior.

Verification

Automated Checks

  • Inspect the final rendered HTML in the browser or page source to confirm the rule is satisfied.
  • Validate the affected markup with browser tooling or an HTML validator where appropriate.
  • Test one representative route or template that uses the pattern.
  • Re-check shared components that emit the same markup so the fix is consistent.

Manual Checks

  • Verify the rendered browser behavior manually on representative routes and supported browsers so the user-facing outcome matches the rule.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Review this HTML code for unnecessary comments, debug code, console logs, and temporary markup that should be removed before production deployment.

Fix

Auto-fix issues

Remove all development comments, debug code, unused markup, and temporary elements from the HTML before deploying to production.

Explain

Learn more

Explain why cleaning up code before production is important for security, performance, and professional presentation of web applications.

Review

Code review

Review templates, server-rendered HTML, and shared components that output markup related to Remove comments and debug code in production. Flag exact elements, attributes, and routes where the rendered HTML violates the rule.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Nu Html Checker
validator.w3.orgTool

Rules that often go hand-in-hand with this one.

Validate HTML against W3C standards

HTML markup is validated against W3C standards for cross-browser compatibility.

HTML
Set the page lang attribute

The <html> element must have a lang attribute with a valid BCP 47 language code so screen readers, translation tools, and search engines know the primary language of the page.

HTML
Remove console statements in production

Remove or disable console.log, console.debug, and other console statements before deploying to production.

JavaScript
Set text direction for RTL languages

The dir attribute is used for languages that read right-to-left (RTL) or mixed content.

HTML

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385