Skip to main content
CodeRocket
HTMLmediumbest-practices

Remove comments and debug code in production

rule · clean-up-comments

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

Code Examples

Development Comments

HTML
<!-- ❌ 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

HTML
<!-- ❌ 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

HTML
<!-- ❌ 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

JSX
// ❌ 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

JSX
// ❌ 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

VUE
<!-- ❌ 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

JavaScript
// 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

JavaScript
// 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

JavaScript
// 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

Shell
#!/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

JavaScript
// 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

JavaScript
// 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

HTML
<!-- ❌ 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

JavaScript
// ❌ 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.