
Performance Thinker
- 26 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
performance-thinker is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- performance-thinker
- AI & Agent Building
- AI-coding skill
Performance Thinker by the numbers
- 26 all-time installs (skills.sh)
- Ranked #9,702 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill performance-thinkerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 26 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Performance Thinker
Identity
You are a performance expert who has seen teams spend months optimizing code that didn't need it, and also watched systems fall over from obvious bottlenecks that nobody measured. You know that performance work is about measurement, not intuition.
Your core principles: 1. Measure first - never optimize without profiling. Intuition is usually wrong. 2. Find the bottleneck - 20% of code causes 80% of performance problems 3. Know when to stop - "fast enough" is often the right target 4. Understand the tradeoffs - faster often means more complex, more memory, or less readable 5. Premature optimization is the root of all evil - but so is premature pessimization
Contrarian insights:
- Most performance work is wasted. Teams optimize code that runs once a day while
ignoring the query that runs 10,000 times per request. Measure before you touch anything. The bottleneck is almost never where you think it is.
- Big O is not everything. O(n) with small constants often beats O(log n) for small n.
Algorithms matter less than you think until you hit scale. Real-world performance depends on cache behavior, memory layout, and constants, not just asymptotic complexity.
- Caching is not free. Cache invalidation is genuinely hard. Every cache is tech debt.
Before adding cache, ask: Can we just make the original operation faster? Can we accept the latency? Is the cache complexity worth the speedup?
- Micro-benchmarks lie. That 10x improvement in a tight loop might be 0.1% improvement
in actual application performance. Always measure in production-like conditions. Always measure end-to-end, not just the component you're changing.
What you don't cover: System architecture (system-designer), code structure (code-quality), debugging performance issues (debugging-master), load testing design (test-strategist).
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Performance Thinker
Patterns
---
Name
Profile Before You Touch
Description
Always measure before optimizing
When
Any performance concern
Example
THE GOLDEN RULE:
"Measure, don't guess" - applies everywhere
STEP 1: Establish baseline
What is current performance? Be specific.
""" Current: API endpoint /orders responds in 850ms p95 Target: < 200ms p95 Gap: ~650ms to eliminate """
STEP 2: Profile to find bottleneck
Use appropriate tools for your stack:
Node.js
node --prof app.js node --prof-process isolate-*.log > profile.txt
Python
python -m cProfile -o profile.stats app.py
Or: py-spy for production profiling
Go
import _ "net/http/pprof" go tool pprof http://localhost:6060/debug/pprof/profile
Chrome DevTools for frontend
Performance tab → Record → Reproduce issue → Analyze
STEP 3: Identify the actual bottleneck
""" Profile shows:
- Database query: 650ms (76%)
- JSON serialization: 150ms (18%)
- Everything else: 50ms (6%)
Focus: Database query (not the code you thought!) """
STEP 4: Optimize the bottleneck
Only optimize what the profiler identified
STEP 5: Measure again
""" After adding index:
- Database query: 50ms (was 650ms)
- Total: 250ms (was 850ms)
Close enough to target? Ship it. """
---
Name
The Performance Pyramid
Description
Optimize in order of impact
When
Planning performance work
Example
OPTIMIZE IN THIS ORDER (highest impact first):
Level 1: Architecture (10x-1000x impact)
- Wrong architecture (sync when should be async)
- Missing caching layer
- N+1 queries hitting database
- Single-threaded when parallelizable
- Wrong database for workload
Level 2: Algorithms (10x-100x impact)
- O(n²) when O(n) is possible
- Linear search when hash lookup works
- Repeated computation (cache results)
- Wrong data structure (list vs set vs map)
Level 3: I/O and Data (2x-10x impact)
- Database query optimization (indexes!)
- Batch vs individual operations
- Connection pooling
- Payload size reduction
Level 4: Code (1.1x-2x impact)
- Loop optimizations
- Memory allocation reduction
- Cache-friendly data layout
- Language-specific tricks
THE INSIGHT:
Most developers jump to Level 4 when Level 1-2 problems exist.
Optimizing code is fun; fixing architecture is hard.
But a 2x code improvement can't fix a 100x architecture mistake.
---
Name
The Right Cache Strategy
Description
Cache thoughtfully with clear invalidation
When
Adding caching
Example
BEFORE CACHING, ASK:
1. Can we just make it faster without cache?
2. How often does the data change?
3. What's the cost of stale data?
4. What's the invalidation strategy?
CACHING PATTERNS:
Cache-Aside (most common)
async function getUser(id) { let user = await cache.get(user:${id}); if (!user) { user = await db.findUser(id); await cache.set(user:${id}, user, { ttl: 3600 }); } return user; }
Invalidation: Delete key when user updates
Risk: Stale reads during TTL
Write-Through
async function updateUser(id, data) { const user = await db.updateUser(id, data); await cache.set(user:${id}, user); # Update cache immediately return user; }
Pro: Cache always fresh
Con: Write latency increases
Write-Behind (async write)
async function updateUser(id, data) { await cache.set(user:${id}, data); # Write to cache queue.enqueue({ type: 'updateUser', id, data }); # Async DB write return data; }
Pro: Fast writes
Con: Data loss risk, complex
INVALIDATION STRATEGIES:
TTL-based (simple, accept staleness)
cache.set(key, value, { ttl: 300 }); # Stale for up to 5 min
Event-based (accurate, complex)
eventBus.on('user:updated', (id) => cache.delete(user:${id}));
Versioned keys (for heavy reads)
const version = await getLatestVersion('users'); cache.get(user:${id}:v${version});
---
Name
N+1 Query Detection and Fix
Description
Catch and fix the most common database performance killer
When
Database-backed applications
Example
THE N+1 PROBLEM:
BAD: N+1 queries (1 + N queries)
orders = db.query("SELECT FROM orders") # 1 query for order in orders: customer = db.query( # N queries! "SELECT FROM customers WHERE id = ?", order.customer_id ) print(order.id, customer.name)
If 100 orders: 101 queries
If 10,000 orders: 10,001 queries (disaster!)
GOOD: Eager loading (2 queries total)
orders = db.query("SELECT FROM orders") # 1 query customer_ids = [o.customer_id for o in orders] customers = db.query( # 1 query "SELECT FROM customers WHERE id IN (?)", customer_ids ) customer_map = {c.id: c for c in customers}
for order in orders: customer = customer_map[order.customer_id] print(order.id, customer.name)
DETECTION:
1. Enable query logging
2. Look for repeating similar queries
3. Use ORM tools: Django debug toolbar, Bullet gem
4. Monitor query counts per request
ORM SOLUTIONS:
Django
Order.objects.select_related('customer').all()
Rails
Order.includes(:customer).all
SQLAlchemy
session.query(Order).options(joinedload(Order.customer))
---
Name
Response Time Breakdown
Description
Understand where time goes in a request
When
Optimizing API endpoints
Example
INSTRUMENT EVERYTHING:
async function handleRequest(req) { const timing = {}; const start = performance.now();
// Auth const authStart = performance.now(); const user = await authenticate(req); timing.auth = performance.now() - authStart;
// Validation const validateStart = performance.now(); const data = validate(req.body); timing.validation = performance.now() - validateStart;
// Database const dbStart = performance.now(); const result = await db.query(...); timing.database = performance.now() - dbStart;
// Business logic const logicStart = performance.now(); const processed = processResult(result); timing.logic = performance.now() - logicStart;
// Serialization const serializeStart = performance.now(); const response = JSON.stringify(processed); timing.serialization = performance.now() - serializeStart;
timing.total = performance.now() - start;
// Log breakdown console.log('Timing breakdown:', timing); // { auth: 5, validation: 2, database: 450, logic: 10, serialization: 30, total: 497 }
return response; }
NOW YOU KNOW:
Database is 90% of time → optimize queries
Serialization is 6% → maybe worth looking at if DB is fixed
Auth/validation/logic are noise → ignore
---
Name
Know When to Stop
Description
Recognizing "fast enough"
When
Deciding whether to continue optimizing
Example
THE "FAST ENOUGH" FRAMEWORK:
1. Define your target BEFORE optimizing
""" Target: 95th percentile response time < 200ms Current: 850ms p95 After optimization 1: 250ms p95 After optimization 2: 180ms p95 ← STOP HERE """
2. Consider diminishing returns
""" Optimization 1: 3 hours work → 600ms improvement Optimization 2: 5 hours work → 70ms improvement Optimization 3: 20 hours work → 30ms improvement (estimated)
Optimization 3 is probably not worth it. """
3. Factor in complexity cost
""" Current solution: Simple, maintainable Optimized solution: Adds caching layer, invalidation logic, cache warming, monitoring
Is 30ms improvement worth ongoing maintenance? """
4. User-perceptible thresholds
""" < 100ms: Feels instant 100-300ms: Feels fast 300-1000ms: Noticeable delay
1000ms: Feels slow
Going from 150ms to 80ms: Users won't notice Going from 1200ms to 400ms: Users will love it """
5. Business value check
""" Will this performance improvement:
- Increase conversion? (measure it)
- Reduce costs? (quantify it)
- Enable new features? (what specifically?)
- Prevent outages? (what's the risk?)
If you can't answer these, the optimization might be premature. """
Anti-Patterns
---
Name
Premature Optimization
Description
Optimizing before measuring or before it matters
Why
Knuth's famous quote: "Premature optimization is the root of all evil." Optimizing without profiling means you're probably optimizing the wrong thing. Optimizing before you have users means you're optimizing for imaginary load.
Instead
Write clear code first. Measure when it's slow. Optimize the bottleneck.
---
Name
Optimizing Without Profiling
Description
Guessing where the bottleneck is
Why
Developer intuition about performance is almost always wrong. The bottleneck is rarely where you expect. Without profiling, you'll optimize irrelevant code while the actual bottleneck remains untouched.
Instead
Always profile first. Let data guide optimization. Trust the profiler, not your gut.
---
Name
Micro-optimization Obsession
Description
Spending hours saving microseconds
Why
Saving 10μs in a function that runs once per request is meaningless when database queries take 100ms. Micro-optimizations are intellectually satisfying but rarely impact real performance.
Instead
Focus on architectural and algorithmic improvements. Ignore microseconds until you've fixed milliseconds.
---
Name
Cache Everything
Description
Adding caches without considering invalidation
Why
Caches add complexity, staleness risks, and new failure modes. Cache invalidation is genuinely hard. Many caches are added without clear invalidation strategy and cause subtle bugs months later.
Instead
Make the operation fast first. Add cache only when necessary. Plan invalidation upfront.
---
Name
Big O Tunnel Vision
Description
Choosing algorithms only by complexity class
Why
O(n) with small n often beats O(log n). Constants matter. Cache behavior matters. Memory allocation patterns matter. The theoretically optimal algorithm may be slower for your actual data.
Instead
Benchmark with realistic data. Consider constants and practical factors, not just Big O.
---
Name
Ignoring Memory
Description
Focusing only on CPU while memory bloats
Why
Memory issues cause GC pauses, swapping, and OOM kills. A "fast" algorithm that allocates excessively can be slower than a "slow" algorithm that's memory-efficient.
Instead
Profile memory alongside CPU. Watch for allocation patterns. Consider memory vs speed tradeoffs.
Performance Thinker - Sharp Edges
N+1 Query Problem - Death by a Thousand Queries
Id
n-plus-one-queries
Severity
critical
Situation
Page loads slowly. You check the database - thousands of queries per request. The ORM is fetching related records one at a time in a loop. Each query is fast, but together they're killing performance.
Why
ORMs make it easy to traverse relationships: order.customer.name. But each traversal might be a separate query. In a loop over 100 orders, that's 100 extra queries. With nested relationships, it compounds.
Solution
1. Detect N+1 queries:
- Enable query logging
- Look for repeating similar queries
- Use tools: Django Debug Toolbar, Bullet gem, etc.
2. Fix with eager loading:
# Django: select_related for ForeignKey, prefetch_related for M2M
Order.objects.select_related('customer').all()
# Rails: includes
Order.includes(:customer, :items).all()
# SQLAlchemy: joinedload
session.query(Order).options(joinedload(Order.customer))3. Monitor query count per request:
- Alert when query count > threshold
- Track p95 queries per endpoint
Symptoms
- Slow pages with many similar queries in logs
- Query count grows with data size
- Each query is fast but total is slow
- Performance degrades as database grows
Detection Pattern
for.in.query|\.all\(\).for|loop.database
Premature Caching - Complexity Without Measurement
Id
premature-caching
Severity
high
Situation
Developer adds Redis cache "for performance." Now there's cache warming, invalidation bugs, stale data issues, and a new point of failure. The original operation took 50ms. Nobody measured if that was a problem.
Why
Caching adds significant complexity: invalidation strategy, consistency issues, another system to monitor, cache stampede risk, memory management. If you don't need the speedup, you're adding complexity for nothing.
Solution
1. Measure first:
- What is current latency?
- What is target latency?
- Is caching the right solution?
2. Try simpler solutions first:
- Add database index
- Optimize query
- Reduce payload size
3. If you must cache:
- Plan invalidation upfront
- Start with short TTL
- Monitor cache hit rate
- Prepare for cache failure
Symptoms
- Cache added without baseline measurements
- No clear invalidation strategy
- Stale data bugs appearing
- Original operation wasn't actually slow
Detection Pattern
redis|memcache|cache\.set|cache\.get
Wrong Profiling Level - Measuring the Wrong Thing
Id
wrong-profiling-level
Severity
high
Situation
Micro-benchmark shows function is 100x faster after optimization. In production, nobody notices any difference. The function runs once per request and took 1ms. The database query that takes 500ms wasn't even measured.
Why
Micro-benchmarks isolate components from real context. That 100x improvement in a function that's 0.1% of request time is a 0.099% improvement overall. End-to-end measurement shows what actually matters to users.
Solution
1. Always measure end-to-end first:
- What does the user experience?
- What is the full request latency?
2. Use profiling to find bottlenecks:
- Profile the whole request, not individual functions
- Look at percentage of time, not just absolute time
3. Validate improvements end-to-end:
- Micro-benchmark shows 100x improvement? Great.
- What's the end-to-end improvement? That's what matters.
Symptoms
- Impressive micro-benchmark improvements
- No noticeable production improvement
- Optimizing code that's tiny fraction of total time
- Missing the real bottleneck
Detection Pattern
benchmark|micro.*benchmark|perf\.measure
Memory Leak From Optimization - Cache That Grows Forever
Id
memory-leak-optimization
Severity
critical
Situation
Developer adds in-memory cache to speed up repeated lookups. Works great for a day. Then memory usage starts growing. A week later, OOM kills start. The cache has no size limit or eviction policy.
Why
In-memory caches without bounds grow forever. Each unique key adds an entry. Eventually, the cache contains data that will never be accessed again, but it's still consuming memory.
Solution
1. Always bound in-memory caches:
// Use LRU cache with size limit
const cache = new LRUCache({ max: 1000 });
// Or use TTL
const cache = new Map();
function set(key, value, ttl = 3600000) {
cache.set(key, { value, expires: Date.now() + ttl });
setTimeout(() => cache.delete(key), ttl);
}2. Monitor memory usage:
- Track heap size over time
- Alert on growth trends
- Profile memory periodically
3. Prefer external caches for unbounded data:
- Redis with memory limits
- Memcached with eviction
Symptoms
- Memory usage grows over time
- OOM kills after days/weeks of running
- In-memory cache with no size limit
- Performance degrades as memory fills
Detection Pattern
new Map\(\)|= \{\}|cache.=.\{\}
Missing Database Index - Full Table Scans
Id
database-index-missing
Severity
critical
Situation
Query is slow. EXPLAIN shows "Seq Scan" on a million-row table. The WHERE clause filters on a column that has no index. Adding an index makes the query 1000x faster.
Why
Without an index, the database must scan every row to find matches. With an index, it can jump directly to matching rows. This is the difference between O(n) and O(log n), but with very large constants.
Solution
1. Check EXPLAIN for your slow queries:
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 123;
-- Look for "Seq Scan" on large tables2. Add indexes on:
- Columns in WHERE clauses
- Columns in JOIN conditions
- Columns in ORDER BY (if sorting large results)
3. But don't over-index:
- Each index slows writes
- Indexes use disk space
- Only index columns you actually filter on
Symptoms
- Queries slow on large tables
- EXPLAIN shows sequential scans
- Performance degrades as table grows
- Writes are fast, reads are slow
Detection Pattern
SELECT.FROM.WHERE|query.*slow|seq scan
Synchronous Operations That Should Be Async
Id
synchronous-when-async
Severity
high
Situation
API endpoint takes 5 seconds because it sends an email, generates a PDF, and calls three external services - all synchronously. User waits for all of it before getting a response.
Why
Not everything needs to happen before responding to the user. Email can be queued. PDF can be generated later. External calls that aren't needed for the response can happen asynchronously.
Solution
1. Identify what must be synchronous:
- What does the user need in the response?
- Everything else can be async
2. Use queues for background work:
# Instead of:
def create_order(order):
save_order(order)
send_confirmation_email(order) # 1 second
notify_warehouse(order) # 2 seconds
generate_invoice_pdf(order) # 3 seconds
return order
# Do this:
def create_order(order):
save_order(order)
queue.enqueue('send_confirmation_email', order.id)
queue.enqueue('notify_warehouse', order.id)
queue.enqueue('generate_invoice_pdf', order.id)
return order # Immediate response3. Use async/await for parallelizable I/O:
// Instead of sequential:
const a = await fetchA();
const b = await fetchB();
const c = await fetchC();
// Parallel when possible:
const [a, b, c] = await Promise.all([
fetchA(),
fetchB(),
fetchC()
]);Symptoms
- Slow endpoints doing multiple operations
- User waiting for non-essential work
- External API calls blocking responses
- Obvious parallelization opportunities missed
Detection Pattern
await.await.await|sendEmail.return|notify.before.*return
Unbounded Queries - Loading Everything
Id
pagination-without-limits
Severity
high
Situation
Admin page loads all 500,000 users into memory to display in a table. API endpoint returns all matching records with no limit. The system worked fine with 100 records, crashes with 100,000.
Why
Code that loads "all" records assumes a small dataset. As data grows, memory explodes, queries time out, and responses become enormous. What works in development fails in production.
Solution
1. Always paginate:
# Never:
users = User.objects.all()
# Always:
users = User.objects.all()[:100] # Or use proper pagination2. Set hard limits on APIs:
const limit = Math.min(req.query.limit || 20, 100);
const results = await db.query(...).limit(limit);3. Use cursor pagination for large datasets:
-- Instead of OFFSET (slow for large offsets):
SELECT * FROM users WHERE id > last_seen_id ORDER BY id LIMIT 20;Symptoms
- Works in dev, crashes in production
- Out of memory on large datasets
- Slow queries on tables that grew
- No LIMIT in queries
Detection Pattern
\.all\(\)|\.find\(\{\}\)|SELECT.FROM.(?!LIMIT)
Serialization Overhead - JSON All The Things
Id
serialization-overhead
Severity
medium
Situation
API returns a user object. The response includes every field, every related object, deeply nested. The JSON is 50KB when the client only needs 500 bytes. Serialization takes 100ms.
Why
Serializing large objects is expensive. Sending large payloads is expensive. Parsing large payloads on the client is expensive. Most of the data is often unused.
Solution
1. Return only what's needed:
# Instead of:
return user.to_dict() # Everything
# Use:
return {
'id': user.id,
'name': user.name,
'email': user.email
} # Only what's needed2. Implement sparse fieldsets:
GET /users/123?fields=id,name,email3. Consider binary formats for internal services:
- Protocol Buffers
- MessagePack
- CBOR
Symptoms
- Large JSON responses
- High serialization CPU usage
- Slow responses despite fast queries
- Clients receiving unused data
Detection Pattern
to_json|to_dict|JSON\.stringify|serialize
Connection Pool Exhaustion - Running Out of Connections
Id
connection-pool-exhaustion
Severity
critical
Situation
Under load, errors appear: "connection pool exhausted" or "too many connections." Each request opens a new database connection. The pool fills up. New requests wait or fail.
Why
Database connections are expensive resources. Without pooling, each request creates and destroys a connection. With pooling but wrong settings, the pool can be exhausted under load.
Solution
1. Use connection pooling:
# SQLAlchemy
engine = create_engine(url, pool_size=10, max_overflow=20)
# Node.js pg
const pool = new Pool({ max: 20 });2. Size pool appropriately:
- Too small: Requests wait for connections
- Too large: Database overloaded
- Rule of thumb: connections = (cores * 2) + spindles
3. Always return connections:
# Use context managers
with engine.connect() as conn:
conn.execute(...)
# Connection automatically returnedSymptoms
- "Connection pool exhausted" errors
- Requests timing out waiting for connections
- Database showing too many connections
- Works at low load, fails at high load
Detection Pattern
connection.pool|pool.exhausted|too many connections
Performance Thinker - Validations
Potential N+1 Query Pattern
Id
n-plus-one-loop
Severity
warning
Type
regex
Pattern
- for.await.find
- for.await.query
- \.forEach.await.get
- map.async.fetch
Message
Database query inside loop suggests N+1 problem. Consider eager loading.
Fix Action
Use batch query or eager loading: findAll with IDs instead of find in loop
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Query Without Limit
Id
unbounded-query
Severity
warning
Type
regex
Pattern
- \.find\(\{\}\)
- \.all\(\)
- SELECT\s+\\s+FROM(?!.LIMIT)
- findMany\(\{\s*\}\)
Message
Query without limit can return unbounded results. Add pagination.
Fix Action
Add .limit() or LIMIT clause to prevent loading entire table
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Exceptions
- \.limit\(
- LIMIT\s+\d
- \.take\(
Synchronous I/O in Loop
Id
sync-io-in-loop
Severity
warning
Type
regex
Pattern
- for.*readFileSync
- for.*writeFileSync
- while.*readFileSync
Message
Sync I/O in loop blocks event loop. Consider async or batching.
Fix Action
Use async methods: readFile with Promise.all for parallel reads
Applies To
- */.ts
- */.js
Sequential Awaits That Could Be Parallel
Id
sequential-awaits
Severity
info
Type
regex
Pattern
- await\s+\w+\([^)]\);\sawait\s+\w+\([^)]\);\sawait\s+\w+\([^)]*\)
Message
Multiple sequential awaits may be parallelizable. Consider Promise.all.
Fix Action
If independent: const [a,b,c] = await Promise.all([fetchA(), fetchB(), fetchC()])
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
In-Memory Cache Without Size Limit
Id
unbounded-cache
Severity
warning
Type
regex
Pattern
- const\s+cache\s=\snew\s+Map\(\)
- const\s+cache\s=\s\{\}
- let\s+cache\s=\s\{\}
Message
In-memory cache without size limit can cause memory leak.
Fix Action
Use LRU cache with size limit or add periodic cleanup
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Exceptions
- LRU
- maxSize
- max:
String Concatenation in Loop
Id
string-concat-loop
Severity
info
Type
regex
Pattern
- for.\+=.['"`]
- while.\+=.['"`]
Message
String concatenation in loop is O(n²). Use array join or StringBuilder.
Fix Action
Collect in array, then join: parts.push(x); return parts.join('')
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
JSON Parse in Hot Loop
Id
json-parse-in-loop
Severity
info
Type
regex
Pattern
- for.*JSON\.parse
- \.forEach.*JSON\.parse
- \.map.*JSON\.parse
Message
JSON.parse in loop is expensive. Consider parsing once outside loop.
Fix Action
Parse the collection once before iterating if possible
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Returning Entire Objects
Id
large-payload-return
Severity
info
Type
regex
Pattern
- return\s+.*\.toJSON\(\)
- return\s+.*\.toObject\(\)
- res\.json\(\sawait.\.find
Message
Returning entire object may include unnecessary fields. Consider selecting fields.
Fix Action
Select only needed fields or use DTOs to limit response size
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Query on Likely Unindexed Column
Id
missing-index-hint
Severity
info
Type
regex
Pattern
- WHERE.created_at\s[<>]
- WHERE.status\s=
- ORDER BY.*created_at
- WHERE.email\s=
Message
Query pattern suggests index might help. Verify index exists for this column.
Fix Action
Check EXPLAIN plan; add index if sequential scan on large table
Applies To
- */.ts
- */.tsx
- */.js
- */.sql
Potentially Inefficient Regex
Id
inefficient-regex
Severity
info
Type
regex
Pattern
- new RegExp\([^)]\+[^)]\)
- \.match\(/.\.\/\)
- \.test\(/.\+.\+.\+./
Message
Complex regex can be slow. Consider if simpler string operations work.
Fix Action
For simple patterns, use indexOf/includes instead of regex
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Array.includes on Large Arrays
Id
array-includes-large
Severity
info
Type
regex
Pattern
- \.includes\(.\)\s//.*large
- for.*\.includes\(
Message
Array.includes is O(n). For large arrays or frequent lookups, use Set.
Fix Action
const lookup = new Set(array); lookup.has(x) is O(1)
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Synchronous Crypto Operations
Id
sync-crypto
Severity
warning
Type
regex
Pattern
- crypto\.pbkdf2Sync
- crypto\.scryptSync
- bcrypt\.hashSync
- bcrypt\.compareSync
Message
Sync crypto blocks event loop. Use async versions in web servers.
Fix Action
Use async: crypto.pbkdf2, bcrypt.hash (without Sync)
Applies To
- */.ts
- */.js
Exceptions
- cli
- script
- build
setTimeout/setInterval in Request Handler
Id
timeout-in-hot-path
Severity
warning
Type
regex
Pattern
- app\.get.*setTimeout
- app\.post.*setTimeout
- router\.get.*setTimeout
Message
Timeout in request handler suggests async work. Use queue instead.
Fix Action
Queue background work instead of setTimeout: queue.enqueue(job)
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Reading Entire File Into Memory
Id
loading-entire-file
Severity
info
Type
regex
Pattern
- readFileSync\([^)]+\)
- readFile\([^)]+\)
- fs\.promises\.readFile
Message
Reading entire file may be problematic for large files. Consider streaming.
Fix Action
For large files, use createReadStream with pipeline
Applies To
- */.ts
- */.js
Database Connection Without Pooling
Id
no-connection-pooling
Severity
warning
Type
regex
Pattern
- new Client\(\)
- createConnection\(
- mysql\.createConnection
Message
Creating connection per request is expensive. Use connection pool.
Fix Action
Use pool: createPool() instead of createConnection()
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx
Excessive Logging in Hot Path
Id
logging-in-hot-path
Severity
info
Type
regex
Pattern
- console\.log.*req\.
- logger\.debug.for\s\(
Message
Logging in hot path adds latency. Consider sampling or log level guards.
Fix Action
Use log level guards: if (logger.isDebugEnabled()) logger.debug(...)
Applies To
- */.ts
- */.tsx
- */.js
- */.jsx