/install best-practices-ecc
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
// ✅ 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
// ✅ 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)
// ✅ 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
// ✅ 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
// ✅ 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
// ✅ GOOD: Proper types
interface Market {
id: string
name: string
status: 'active' | 'resolved' | 'closed'
created_at: Date
}
function getMarket(id: string): Promise\x3CMarket> {
// Implementation
}
// ❌ BAD: Using 'any'
function getMarket(id: any): Promise\x3Cany> {
// Implementation
}
React Best Practices
Component Structure
// ✅ 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 (
\x3Cbutton
onClick={onClick}
disabled={disabled}
className={`btn btn-${variant}`}
>
{children}
\x3C/button>
)
}
// ❌ BAD: No types, unclear structure
export function Button(props) {
return \x3Cbutton onClick={props.onClick}>{props.children}\x3C/button>
}
Custom Hooks
// ✅ GOOD: Reusable custom hook
export function useDebounce\x3CT>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState\x3CT>(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:
### 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:
// VS Code settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
}
Pre-Commit Hooks
// 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:
// .claude/settings.json
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "npm run lint:fix"
}]
}]
}
}
Security
HTTPS everywhere
Enforce HTTPS:
\x3C!-- ❌ Mixed content -->
\x3Cimg src="http://example.com/image.jpg">
\x3Cscript src="http://cdn.example.com/script.js">\x3C/script>
\x3C!-- ✅ HTTPS only -->
\x3Cimg src="https://example.com/image.jpg">
\x3Cscript src="https://cdn.example.com/script.js">\x3C/script>
\x3C!-- ✅ Protocol-relative (will use page's protocol) -->
\x3Cimg src="//example.com/image.jpg">
HSTS Header:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content Security Policy (CSP)
\x3C!-- Basic CSP via meta tag -->
\x3Cmeta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;">
\x3C!-- Better: HTTP header -->
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:
\x3Cscript nonce="abc123">
// This inline script is allowed
\x3C/script>
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
# Check for vulnerabilities
npm audit
yarn audit
# Auto-fix when possible
npm audit fix
# Check specific package
npm ls lodash
Keep dependencies updated:
// package.json
{
"scripts": {
"audit": "npm audit --audit-level=moderate",
"update": "npm update && npm audit fix"
}
}
Known vulnerable patterns to avoid:
// ❌ Prototype pollution vulnerable patterns
Object.assign(target, userInput);
_.merge(target, userInput);
// ✅ Safer alternatives
const safeData = JSON.parse(JSON.stringify(userInput));
Input sanitization
// ❌ 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
// ❌ Insecure cookie
document.cookie = "session=abc123";
// ✅ Secure cookie (server-side)
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/
Browser compatibility
Doctype declaration
\x3C!-- ❌ Missing or invalid doctype -->
\x3CHTML>
\x3C!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
\x3C!-- ✅ HTML5 doctype -->
\x3C!DOCTYPE html>
\x3Chtml lang="en">
Character encoding
\x3C!-- ❌ Missing or late charset -->
\x3Chtml>
\x3Chead>
\x3Ctitle>Page\x3C/title>
\x3Cmeta charset="UTF-8">
\x3C/head>
\x3C!-- ✅ Charset as first element in head -->
\x3Chtml>
\x3Chead>
\x3Cmeta charset="UTF-8">
\x3Ctitle>Page\x3C/title>
\x3C/head>
Viewport meta tag
\x3C!-- ❌ Missing viewport -->
\x3Chead>
\x3Ctitle>Page\x3C/title>
\x3C/head>
\x3C!-- ✅ Responsive viewport -->
\x3Chead>
\x3Cmeta charset="UTF-8">
\x3Cmeta name="viewport" content="width=device-width, initial-scale=1">
\x3Ctitle>Page\x3C/title>
\x3C/head>
Feature detection
// ❌ 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)
\x3C!-- Load polyfills conditionally -->
\x3Cscript>
if (!('fetch' in window)) {
document.write('\x3Cscript src="/polyfills/fetch.js">\x3C\/script>');
}
\x3C/script>
\x3C!-- Or use polyfill.io -->
\x3Cscript src="https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver">\x3C/script>
Deprecated APIs
Avoid these
// ❌ document.write (blocks parsing)
document.write('\x3Cscript src="...">\x3C/script>');
// ✅ 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)
\x3Chtml manifest="cache.manifest">
// ✅ Service Workers
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
Event listener passive
// ❌ 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
// ❌ 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)
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 \x3CFallbackUI />;
}
return this.props.children;
}
}
// Usage
\x3CErrorBoundary>
\x3CApp />
\x3C/ErrorBoundary>
Global error handler
// 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
// ❌ 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
// ❌ Blocking script
\x3Cscript src="heavy-library.js">\x3C/script>
// ✅ Deferred script
\x3Cscript defer src="heavy-library.js">\x3C/script>
// ❌ Blocking CSS import
@import url('other-styles.css');
// ✅ Link tags (parallel loading)
\x3Clink rel="stylesheet" href="styles.css">
\x3Clink rel="stylesheet" href="other-styles.css">
Efficient event handlers
// ❌ 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
// ❌ 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
\x3C!-- ❌ Invalid HTML -->
\x3Cdiv id="header">
\x3Cdiv id="header"> \x3C!-- Duplicate ID -->
\x3Cul>
\x3Cdiv>Item\x3C/div> \x3C!-- Invalid child -->
\x3C/ul>
\x3Ca href="/">\x3Cbutton>Click\x3C/button>\x3C/a> \x3C!-- Invalid nesting -->
\x3C!-- ✅ Valid HTML -->
\x3Cheader id="site-header">
\x3C/header>
\x3Cul>
\x3Cli>Item\x3C/li>
\x3C/ul>
\x3Ca href="/" class="button">Click\x3C/a>
Semantic HTML
\x3C!-- ❌ Non-semantic -->
\x3Cdiv class="header">
\x3Cdiv class="nav">
\x3Cdiv class="nav-item">Home\x3C/div>
\x3C/div>
\x3C/div>
\x3Cdiv class="main">
\x3Cdiv class="article">
\x3Cdiv class="title">Headline\x3C/div>
\x3C/div>
\x3C/div>
\x3C!-- ✅ Semantic HTML5 -->
\x3Cheader>
\x3Cnav>
\x3Ca href="/">Home\x3C/a>
\x3C/nav>
\x3C/header>
\x3Cmain>
\x3Carticle>
\x3Ch1>Headline\x3C/h1>
\x3C/article>
\x3C/main>
Image aspect ratios
\x3C!-- ❌ Distorted images -->
\x3Cimg src="photo.jpg" width="300" height="100">
\x3C!-- If actual ratio is 4:3, this squishes the image -->
\x3C!-- ✅ Preserve aspect ratio -->
\x3Cimg src="photo.jpg" width="300" height="225">
\x3C!-- Actual 4:3 dimensions -->
\x3C!-- ✅ CSS object-fit for flexibility -->
\x3Cimg src="photo.jpg" style="width: 300px; height: 200px; object-fit: cover;">
Permissions & privacy
Request permissions properly
// ❌ Request on page load (bad UX, often denied)
navigator.geolocation.getCurrentPosition(success, error);
// ✅ Request in context, after user action
findNearbyButton.addEventListener('click', async () => {
// Explain why you need it
if (await showPermissionExplanation()) {
navigator.geolocation.getCurrentPosition(success, error);
}
});
Permissions policy
\x3C!-- Restrict powerful features -->
\x3Cmeta http-equiv="Permissions-Policy"
content="geolocation=(), camera=(), microphone=()">
\x3C!-- Or allow for specific origins -->
\x3Cmeta http-equiv="Permissions-Policy"
content="geolocation=(self 'https://maps.example.com')">
Audit checklist
Security (critical)
- HTTPS enabled, no mixed content
- No vulnerable dependencies (
npm audit) - CSP headers configured
- Security headers present
- No exposed source maps
Compatibility
- Valid HTML5 doctype
- Charset declared first in head
- Viewport meta tag present
- No deprecated APIs used
- Passive event listeners for scroll/touch
Code quality
- No console errors
- Valid HTML (no duplicate IDs)
- Semantic HTML elements used
- Proper error handling
- Memory cleanup in components
UX
- No intrusive interstitials
- Permission requests in context
- Clear error messages
- Appropriate image aspect ratios
Tools
| Tool | Purpose |
|---|---|
npm audit |
Dependency vulnerabilities |
| SecurityHeaders.com | Header analysis |
| W3C Validator | HTML validation |
| Lighthouse | Best practices audit |
| Observatory | Security scan |
References
- Make sure OpenClaw is installed (local or Docker)
- Run the install command in chat:
/install best-practices-ecc - After installation, invoke the skill by name or use
/best-practices-ecc - Provide required inputs per the skill's parameter spec and get structured output
What is Best Practices?
Apply modern web development best practices for security, compatibility, code quality, and coding standards. Covers KISS/DRY/YAGNI principles, TypeScript/Jav... It is an AI Agent Skill for Claude Code / OpenClaw, with 340 downloads so far.
How do I install Best Practices?
Run "/install best-practices-ecc" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.
Is Best Practices free?
Yes, Best Practices is completely free (open-source). You can download, install and use it at no cost.
Which platforms does Best Practices support?
Best Practices is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).
Who created Best Practices?
It is built and maintained by huamu668 (@huamu668); the current version is v2.0.0.