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> </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
- Use Environment Variables: Different configs for dev/prod
- Automate Cleanup: Build process handles code cleaning
- Code Reviews: Check for leftover debug code
- Linting Rules: Prevent console.log in production code
- Source Maps: Keep debugging capability without exposing source
- Feature Flags: Control features without code comments
- 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.