Performance Review Analyzer
Quest 86: Performance Review Analyzer
hard 30-45 minutes🎯 Learning Objectives
- How to detect common performance anti-patterns programmatically
- Why you should Measure Before Optimizing — not every slow-looking code is actually slow
- How to identify N+1 queries, sync blocking, memory leaks, and unnecessary re-renders
- The difference between module-level and function-level code context
📖 Concept: Performance Anti-Patterns
Performance problems are some of the hardest bugs to diagnose in production. By the time you notice slowness, the code is in production, under load, and hard to reproduce. Performance review analysis catches these issues during code review — before they ever reach production.
The key principle is Measure Before Optimizing. A performance analyzer doesn’t auto-fix anything — it flags suspicious patterns and suggests measurement. Some anti-patterns are always bad (N+1 queries), while others are context-dependent (readFileSync at module level for config is fine; readFileSync inside a request handler is not).
Think of performance anti-patterns as landmines in the code — most of the time you’re fine walking, but when you hit one under load, the explosion is devastating and hard to trace back.
⚙️ How It Works
Common Performance Anti-Patterns
| Pattern | Severity | Example |
|---|---|---|
| N+1 queries | critical | Nested loops accessing DB inside outer loop |
| Sync blocking | high | readFileSync inside async function |
| Memory leak | high | addEventListener without removeEventListener |
| Unnecessary re-render | high | setState inside a loop |
| Large payload | medium | JSON.parse(JSON.stringify(obj)) for deep clone |
Context-Dependent Detection
readFileSync at module level (config loading) → ✅ AcceptablereadFileSync inside a request handler → ❌ Anti-patternaddEventListener in component mount → ✅ NormaladdEventListener without matching cleanup → ❌ Memory leakThe challenge: your detector needs to understand where in the code an anti-pattern appears, not just whether it exists.
💡 Example: Detecting N+1 Queries
The N+1 query problem is the most common performance anti-pattern in web applications:
// ❌ N+1 QUERY — fetches users, then queries DB for each user's ordersasync function getUserOrders(users) { const results = []; for (const user of users) { const orders = await db.query(`SELECT * FROM orders WHERE user_id = ${user.id}`); results.push({ user, orders }); } return results;}
// ✅ BETTER — single query with JOINasync function getUserOrders(users) { const ids = users.map(u => u.id); return db.query(`SELECT * FROM orders WHERE user_id IN (?)`, [ids]);}A detector looks for: nested loops where the inner loop contains a DB call, fetch, or async operation.
⚠️ Common Mistakes
Mistake 1: Flagging ALL readFileSync calls
“readFileSync is always blocking!” →
readFileSyncat module level (for config) is acceptable. It only becomes a problem when called inside request handlers or loops where it blocks the event loop.
Mistake 2: Not distinguishing module-level from function-level code
“I see readFileSync, so it’s a problem” → Check whether the call is inside a function body or at the top level. Module-level sync operations run once at startup — that’s usually fine.
Mistake 3: Treating all findings as “fix immediately”
“Every performance issue is critical” →
JSON.parse(JSON.stringify())for deep clone is medium severity. N+1 queries are critical. Prioritize so teams tackle the real problems first.
Mistake 4: Missing the context around event listeners
“Found addEventListener → memory leak” → Check if there’s a matching
removeEventListener. AnaddEventListenerwith proper cleanup is normal, not a leak.
📝 Knowledge Check
📝 Knowledge Check
Q1:Why is `readFileSync` at module level generally acceptable, but inside a request handler it's not?
Q2:What is the N+1 query problem?
Q3:Why is `JSON.parse(JSON.stringify(obj))` considered a performance anti-pattern?
🏋️ Quest: Performance Review Analyzer
Now it’s time to practice! Build a performance anti-pattern detector.
-
Download ไฟล์เริ่มต้นของ quest:
Terminal window npx bluebeltdojo download quest-86-performance-reviewcd quest-86-performance-review -
เปิด
problem.jsใน editor ของคุณพร้อมความช่วยเหลือของ AI -
Implement the
analyzePerformance(code)function that detects:- n-plus-one: nested loops with DB/fetch calls inside
- sync-blocking:
readFileSync/writeFileSyncinside async functions - memory-leak:
addEventListenerwithoutremoveEventListener - large-payload:
JSON.parse(JSON.stringify())for deep clone
-
Critical edge case:
readFileSyncat module level (outside functions) for config loading is acceptable — don’t flag it -
ตรวจสอบ solution ของคุณ:
Terminal window node test.js -
When all tests pass, submit your solution:
Terminal window npx bluebeltdojo submit
💡 Tip: This is a hard quest. Start by detecting the simplest patterns (large-payload), then work up to context-dependent ones (sync-blocking).
คำใบ้
- เริ่มจาก pattern ที่ง่ายที่สุดก่อน เช่น
JSON.parse(JSON.stringify()) readFileSyncที่ module level ≠readFileSyncใน function — ต้องตรวจสอบ context- ตรวจสอบ
addEventListenerว่ามีremoveEventListenerคู่กันหรือไม่ - ใช้ approach ทีละ pattern — อย่าพยายามทำทุกอย่างพร้อมกัน
- ถ้าติดขัด ลองเขียน code ที่มี anti-pattern แล้ว test detector ของคุณ