--- name: best-practices description: Apply modern web development best practices for security, compatibility, code quality, and coding standards. Covers KISS/DRY/YAGNI principles, TypeScript/JavaScript standards, React patterns, write-time quality enforcement, and comprehensive code review checklists. license: MIT metadata: author: web-quality-skills version: "2.0" origin: "ECC + web-quality-skills" --- # Best Practices Modern web development standards based on Lighthouse best practices audits and production-proven coding standards. Covers security, browser compatibility, code quality patterns, and write-time enforcement. ## Code Quality Principles ### 1. Readability First - Code is read more than written - Clear variable and function names - Self-documenting code preferred over comments - Consistent formatting ### 2. KISS (Keep It Simple, Stupid) - Simplest solution that works - Avoid over-engineering - No premature optimization - Easy to understand > clever code ### 3. DRY (Don't Repeat Yourself) - Extract common logic into functions - Create reusable components - Share utilities across modules - Avoid copy-paste programming ### 4. YAGNI (You Aren't Gonna Need It) - Don't build features before they're needed - Avoid speculative generality - Add complexity only when required - Start simple, refactor when needed --- ## TypeScript/JavaScript Standards ### Variable Naming ```typescript // ✅ GOOD: Descriptive names const marketSearchQuery = 'election' const isUserAuthenticated = true const totalRevenue = 1000 // ❌ BAD: Unclear names const q = 'election' const flag = true const x = 1000 ``` ### Function Naming ```typescript // ✅ GOOD: Verb-noun pattern async function fetchMarketData(marketId: string) { } function calculateSimilarity(a: number[], b: number[]) { } function isValidEmail(email: string): boolean { } // ❌ BAD: Unclear or noun-only async function market(id: string) { } function similarity(a, b) { } function email(e) { } ``` ### Immutability Pattern (CRITICAL) ```typescript // ✅ ALWAYS use spread operator const updatedUser = { ...user, name: 'New Name' } const updatedArray = [...items, newItem] // ❌ NEVER mutate directly user.name = 'New Name' // BAD items.push(newItem) // BAD ``` ### Error Handling ```typescript // ✅ GOOD: Comprehensive error handling async function fetchData(url: string) { try { const response = await fetch(url) if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`) } return await response.json() } catch (error) { console.error('Fetch failed:', error) throw new Error('Failed to fetch data') } } // ❌ BAD: No error handling async function fetchData(url) { const response = await fetch(url) return response.json() } ``` ### Async/Await Best Practices ```typescript // ✅ GOOD: Parallel execution when possible const [users, markets, stats] = await Promise.all([ fetchUsers(), fetchMarkets(), fetchStats() ]) // ❌ BAD: Sequential when unnecessary const users = await fetchUsers() const markets = await fetchMarkets() const stats = await fetchStats() ``` ### Type Safety ```typescript // ✅ GOOD: Proper types interface Market { id: string name: string status: 'active' | 'resolved' | 'closed' created_at: Date } function getMarket(id: string): Promise { // Implementation } // ❌ BAD: Using 'any' function getMarket(id: any): Promise { // Implementation } ``` --- ## React Best Practices ### Component Structure ```typescript // ✅ GOOD: Functional component with types interface ButtonProps { children: React.ReactNode onClick: () => void disabled?: boolean variant?: 'primary' | 'secondary' } export function Button({ children, onClick, disabled = false, variant = 'primary' }: ButtonProps) { return ( ) } // ❌ BAD: No types, unclear structure export function Button(props) { return } ``` ### Custom Hooks ```typescript // ✅ GOOD: Reusable custom hook export function useDebounce(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value) useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value) }, delay) return () => { clearTimeout(handler) } }, [value, delay]) return debouncedValue } // Usage const debouncedSearch = useDebounce(searchQuery, 300) ``` --- ## Code Review Checklist ### Pre-Review Self-Check Before submitting code for review: ```markdown ### Functionality - [ ] Code works as intended - [ ] Edge cases handled - [ ] Error paths tested - [ ] No hardcoded secrets or values ### Code Quality - [ ] Follows naming conventions - [ ] No code duplication (DRY) - [ ] Functions are focused (single responsibility) - [ ] No unnecessary complexity (KISS) - [ ] No speculative features (YAGNI) ### TypeScript/JavaScript - [ ] Proper types (no `any`) - [ ] Null checks where needed - [ ] Async/await used correctly - [ ] No mutation of props/state ### React - [ ] Proper hook dependencies - [ ] No memory leaks (cleanup) - [ ] Keys provided for lists - [ ] Accessibility attributes ### Testing - [ ] Unit tests added/updated - [ ] Integration tests if needed - [ ] All tests passing - [ ] No test coverage gaps ### Documentation - [ ] Complex logic commented - [ ] API changes documented - [ ] README updated if needed ``` --- ## Write-Time Code Quality ### Auto-Format on Save Configure your editor to format on save: ```json // VS Code settings.json { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[typescript]": { "editor.defaultFormatter": "vscode.typescript-language-features" } } ``` ### Pre-Commit Hooks ```json // package.json { "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.{ts,tsx}": ["eslint --fix", "prettier --write"], "*.{js,jsx}": ["eslint --fix", "prettier --write"] } } ``` ### Claude Code Hook Integration For automatic quality enforcement during Claude Code sessions: ```json // .claude/settings.json { "hooks": { "PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "npm run lint:fix" }] }] } } ``` --- ## Security ### HTTPS everywhere **Enforce HTTPS:** ```html ``` **HSTS Header:** ``` Strict-Transport-Security: max-age=31536000; includeSubDomains; preload ``` ### Content Security Policy (CSP) ```html ``` **CSP Header (recommended):** ``` Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123' https://trusted.com; style-src 'self' 'nonce-abc123'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; ``` **Using nonces for inline scripts:** ```html ``` ### Security headers ``` # Prevent clickjacking X-Frame-Options: DENY # Prevent MIME type sniffing X-Content-Type-Options: nosniff # Enable XSS filter (legacy browsers) X-XSS-Protection: 1; mode=block # Control referrer information Referrer-Policy: strict-origin-when-cross-origin # Permissions policy (formerly Feature-Policy) Permissions-Policy: geolocation=(), microphone=(), camera=() ``` ### No vulnerable libraries ```bash # Check for vulnerabilities npm audit yarn audit # Auto-fix when possible npm audit fix # Check specific package npm ls lodash ``` **Keep dependencies updated:** ```json // package.json { "scripts": { "audit": "npm audit --audit-level=moderate", "update": "npm update && npm audit fix" } } ``` **Known vulnerable patterns to avoid:** ```javascript // ❌ Prototype pollution vulnerable patterns Object.assign(target, userInput); _.merge(target, userInput); // ✅ Safer alternatives const safeData = JSON.parse(JSON.stringify(userInput)); ``` ### Input sanitization ```javascript // ❌ XSS vulnerable element.innerHTML = userInput; document.write(userInput); // ✅ Safe text content element.textContent = userInput; // ✅ If HTML needed, sanitize import DOMPurify from 'dompurify'; element.innerHTML = DOMPurify.sanitize(userInput); ``` ### Secure cookies ```javascript // ❌ Insecure cookie document.cookie = "session=abc123"; // ✅ Secure cookie (server-side) Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/ ``` --- ## Browser compatibility ### Doctype declaration ```html ``` ### Character encoding ```html Page Page ``` ### Viewport meta tag ```html Page Page ``` ### Feature detection ```javascript // ❌ Browser detection (brittle) if (navigator.userAgent.includes('Chrome')) { // Chrome-specific code } // ✅ Feature detection if ('IntersectionObserver' in window) { // Use IntersectionObserver } else { // Fallback } // ✅ Using @supports in CSS @supports (display: grid) { .container { display: grid; } } @supports not (display: grid) { .container { display: flex; } } ``` ### Polyfills (when needed) ```html ``` --- ## Deprecated APIs ### Avoid these ```javascript // ❌ document.write (blocks parsing) document.write(''); // ✅ Dynamic script loading const script = document.createElement('script'); script.src = '...'; document.head.appendChild(script); // ❌ Synchronous XHR (blocks main thread) const xhr = new XMLHttpRequest(); xhr.open('GET', url, false); // false = synchronous // ✅ Async fetch const response = await fetch(url); // ❌ Application Cache (deprecated) // ✅ Service Workers if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); } ``` ### Event listener passive ```javascript // ❌ Non-passive touch/wheel (may block scrolling) element.addEventListener('touchstart', handler); element.addEventListener('wheel', handler); // ✅ Passive listeners (allows smooth scrolling) element.addEventListener('touchstart', handler, { passive: true }); element.addEventListener('wheel', handler, { passive: true }); // ✅ If you need preventDefault, be explicit element.addEventListener('touchstart', handler, { passive: false }); ``` --- ## Console & errors ### No console errors ```javascript // ❌ Errors in production console.log('Debug info'); // Remove in production throw new Error('Unhandled'); // Catch all errors // ✅ Proper error handling try { riskyOperation(); } catch (error) { // Log to error tracking service errorTracker.captureException(error); // Show user-friendly message showErrorMessage('Something went wrong. Please try again.'); } ``` ### Error boundaries (React) ```jsx class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, info) { errorTracker.captureException(error, { extra: info }); } render() { if (this.state.hasError) { return ; } return this.props.children; } } // Usage ``` ### Global error handler ```javascript // Catch unhandled errors window.addEventListener('error', (event) => { errorTracker.captureException(event.error); }); // Catch unhandled promise rejections window.addEventListener('unhandledrejection', (event) => { errorTracker.captureException(event.reason); }); ``` --- ## Source maps ### Production configuration ```javascript // ❌ Source maps exposed in production // webpack.config.js module.exports = { devtool: 'source-map', // Exposes source code }; // ✅ Hidden source maps (uploaded to error tracker) module.exports = { devtool: 'hidden-source-map', }; // ✅ Or no source maps in production module.exports = { devtool: process.env.NODE_ENV === 'production' ? false : 'source-map', }; ``` --- ## Performance best practices ### Avoid blocking patterns ```javascript // ❌ Blocking script // ✅ Deferred script // ❌ Blocking CSS import @import url('other-styles.css'); // ✅ Link tags (parallel loading) ``` ### Efficient event handlers ```javascript // ❌ Handler on every element items.forEach(item => { item.addEventListener('click', handleClick); }); // ✅ Event delegation container.addEventListener('click', (e) => { if (e.target.matches('.item')) { handleClick(e); } }); ``` ### Memory management ```javascript // ❌ Memory leak (never removed) const handler = () => { /* ... */ }; window.addEventListener('resize', handler); // ✅ Cleanup when done const handler = () => { /* ... */ }; window.addEventListener('resize', handler); // Later, when component unmounts: window.removeEventListener('resize', handler); // ✅ Using AbortController const controller = new AbortController(); window.addEventListener('resize', handler, { signal: controller.signal }); // Cleanup: controller.abort(); ``` --- ## Code quality ### Valid HTML ```html