{ "dimension": "performance", "prefix": "PERF", "description": "Rules for detecting performance issues including inefficient algorithms, memory leaks, and resource waste", "rules": [ { "id": "nested-loops", "category": "algorithm-complexity", "severity": "medium", "pattern": "for\\s*\\([^)]+\\)\\s*\\{[^}]*for\\s*\\([^)]+\\)|forEach\\s*\\([^)]+\\)\\s*\\{[^}]*forEach", "patternType": "regex", "description": "Nested loops may indicate O(n^2) or worse complexity. Consider if this can be optimized", "recommendation": "Use Map/Set for O(1) lookups, break early when possible, or restructure the algorithm", "fixExample": "// Before - O(n^2)\nfor (const a of listA) {\n for (const b of listB) {\n if (a.id === b.id) { ... }\n }\n}\n\n// After - O(n)\nconst bMap = new Map(listB.map(b => [b.id, b]));\nfor (const a of listA) {\n const b = bMap.get(a.id);\n if (b) { ... }\n}" }, { "id": "array-in-loop", "category": "inefficient-operation", "severity": "high", "pattern": "\\.includes\\s*\\(|indexOf\\s*\\(|find\\s*\\(", "patternType": "includes", "contextPattern": "for|while|forEach|map|filter|reduce", "description": "Array search methods inside loops cause O(n*m) complexity. Consider using Set or Map", "recommendation": "Convert array to Set before the loop for O(1) lookups", "fixExample": "// Before - O(n*m)\nfor (const item of items) {\n if (existingIds.includes(item.id)) { ... }\n}\n\n// After - O(n)\nconst idSet = new Set(existingIds);\nfor (const item of items) {\n if (idSet.has(item.id)) { ... }\n}" }, { "id": "synchronous-io", "category": "io-efficiency", "severity": "high", "pattern": "readFileSync|writeFileSync|execSync|spawnSync", "patternType": "includes", "description": "Synchronous I/O blocks the event loop and degrades application responsiveness", "recommendation": "Use async versions (readFile, writeFile) or Promise-based APIs", "fixExample": "// Before\nconst data = fs.readFileSync(path);\n\n// After\nconst data = await fs.promises.readFile(path);\n// or\nfs.readFile(path, (err, data) => { ... });" }, { "id": "memory-leak-closure", "category": "memory-leak", "severity": "high", "pattern": "addEventListener\\s*\\(|setInterval\\s*\\(|setTimeout\\s*\\(", "patternType": "regex", "negativePatterns": ["removeEventListener", "clearInterval", "clearTimeout"], "description": "Event listeners and timers without cleanup can cause memory leaks", "recommendation": "Always remove event listeners and clear timers in cleanup functions (componentWillUnmount, useEffect cleanup)", "fixExample": "// Before\nuseEffect(() => {\n window.addEventListener('resize', handler);\n}, []);\n\n// After\nuseEffect(() => {\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n}, []);" }, { "id": "unnecessary-rerender", "category": "react-performance", "severity": "medium", "pattern": "useState\\s*\\(\\s*\\{|useState\\s*\\(\\s*\\[", "patternType": "regex", "description": "Creating new object/array references in useState can cause unnecessary re-renders", "recommendation": "Use useMemo for computed values, useCallback for functions, or consider state management libraries", "fixExample": "// Before - new object every render\nconst [config] = useState({ theme: 'dark' });\n\n// After - stable reference\nconst defaultConfig = useMemo(() => ({ theme: 'dark' }), []);\nconst [config] = useState(defaultConfig);" } ] }