DOMAIN:PERFORMANCE:PITFALLS¶
OWNER: nessa
UPDATED: 2026-03-24
SCOPE: common performance mistakes and how to avoid them
PITFALL:PREMATURE_OPTIMIZATION¶
ANTI_PATTERN: optimizing code before measuring where the bottleneck is
FIX: always profile first — measure p95, identify hottest path, optimize that
RULE: "premature optimization is the root of all evil" (Knuth) — but measured optimization is engineering
PITFALL:LOAD_TEST_ON_WRONG_ENVIRONMENT¶
ANTI_PATTERN: load testing development environment and extrapolating to production
FIX: load test staging environment with production-equivalent resources
RULE: if staging has 1 CPU and prod has 4, results are useless
RULE: seed staging with realistic data volume — empty database performs very differently
PITFALL:IGNORING_COLD_START¶
ANTI_PATTERN: measuring only warm cache performance
FIX: include cold start in load test (first request after deploy)
CHECK: JIT compilation warm-up (Node.js optimizes hot functions after ~1000 calls)
CHECK: connection pool warm-up (first requests establish connections)
CHECK: DNS cache warm-up (first resolution is slower)
CHECK: application cache warm-up (Redis/memory cache empty after restart)
PITFALL:AVERAGES_HIDE_PROBLEMS¶
ANTI_PATTERN: reporting average response time as the performance metric
FIX: use percentiles (p50, p95, p99) — average hides tail latency
EXAMPLE: 99 requests at 100ms + 1 request at 10s = average 199ms — looks fine, but 1% of users wait 10 seconds
PITFALL:TESTING_HAPPY_PATH_ONLY¶
ANTI_PATTERN: load testing only the fastest endpoint
FIX: test realistic user journeys — mix of reads, writes, searches, uploads
FIX: include error paths — how does the system behave when some requests fail?
FIX: include authentication — token validation adds latency to every request
PITFALL:MISSING_INDEXES_IN_STAGING¶
ANTI_PATTERN: staging database has different indexes than production
FIX: staging must use identical schema including indexes
FIX: performance tests should run EXPLAIN ANALYZE on critical queries
PITFALL:OVER_INDEXING¶
ANTI_PATTERN: adding an index for every slow query without considering write impact
FIX: each index slows INSERT/UPDATE/DELETE — measure write impact too
FIX: use composite indexes instead of multiple single-column indexes
FIX: use partial indexes when queries have constant conditions
PITFALL:UNBOUNDED_QUERIES¶
ANTI_PATTERN: SELECT * FROM large_table without LIMIT
FIX: always paginate — LIMIT + OFFSET or cursor-based pagination
FIX: select only needed columns — SELECT col1, col2 not SELECT *
FIX: add query timeout at application level (5s max for web requests)
PITFALL:SYNC_OPERATIONS_IN_ASYNC_CODE¶
ANTI_PATTERN: fs.readFileSync, JSON.parse of large payloads, or crypto operations blocking event loop
FIX: use async variants (fs.readFile, streaming JSON parsers)
FIX: offload CPU-intensive work to worker threads
FIX: set up event loop monitoring to detect blockage
TOOL: detect event loop blocking in Node.js
let lastCheck = Date.now();
setInterval(() => {
const now = Date.now();
const delay = now - lastCheck - 1000;
if (delay > 100) console.warn(`Event loop blocked for ${delay}ms`);
lastCheck = now;
}, 1000);
PITFALL:MEMORY_LEAK_IN_SSR¶
ANTI_PATTERN: storing per-request state in module-level variables in Next.js SSR
FIX: all request state must be scoped to the request handler
FIX: never use global/module-level Maps or arrays that grow with each request
FIX: use WeakMap if caching is needed — allows GC of unused entries
CHECK: monitor pod RSS memory over time — should be flat, not growing
PITFALL:CDN_CACHE_BYPASS¶
ANTI_PATTERN: cache-busting query strings on every request, making CDN useless
FIX: use content-hash in filenames (Next.js does this by default for static assets)
FIX: set appropriate Cache-Control headers (immutable for hashed assets)
FIX: verify CDN cache hit ratio — should be > 80% for static assets
PITFALL:FRONTEND_BUNDLE_BLOAT¶
ANTI_PATTERN: importing entire libraries when only one function is needed
FIX: use tree-shakeable imports (import { debounce } from 'lodash-es', NOT import _ from 'lodash')
FIX: analyze bundle: npx next build && npx @next/bundle-analyzer
FIX: lazy load below-the-fold components with dynamic imports
FIX: check for duplicate dependencies: npx depcheck
PITFALL:WATERFALL_REQUESTS¶
ANTI_PATTERN: sequential API calls that could be parallel
FIX: use Promise.all for independent requests
FIX: use React Suspense + parallel data fetching patterns
FIX: server-side: batch independent DB queries with Promise.all
PITFALL:NO_CONNECTION_POOLING¶
ANTI_PATTERN: creating new database connection per request
FIX: use connection pool (pg-pool for PostgreSQL)
FIX: pool size per pod = max_connections / num_pods - buffer
FIX: set idle timeout to prevent stale connections
PITFALL:COMPRESSION_DISABLED¶
ANTI_PATTERN: serving uncompressed responses over the network
FIX: enable gzip/brotli compression for text responses (HTML, JSON, CSS, JS)
FIX: verify with: curl -H "Accept-Encoding: gzip" -sI <url> | grep Content-Encoding
FIX: compress at CDN/reverse proxy level, not application level (saves CPU)
NOTE: don't compress already-compressed formats (images, videos, zip files)