"I've been pulled into 30+ performance fire drills. These 7 patterns show up every time. Frequently ignored until it's too late."
Performance doesn't fail loud. It fails slow, intermittently, and then all at once. These are the silent killers I see across almost every under-performing system—regardless of stack, industry, or team size.
Run your system through this list. Find one? You're sitting on a fuse.
1. Hidden I/O Blocking
Async isn't always async. That database call wrapped in Task.Run()? It's still blocking under the hood.
Symptoms:
- High thread pool exhaustion
- Long tail latencies on simple requests
- "Hangs" under minimal load
Fix it: Audit async boundaries. Use profiling tools to trace actual blocking. Replace lazy abstraction with intentional I/O flow.
2. Silent UI Polling
Your frontend is hammering endpoints every few seconds, and no one knows. Until your backend crashes.
Symptoms:
- Constant baseline traffic with no user interaction
- Bursty backend load during idle periods
- "Why are we doing 10x the requests we expected?"
Fix it:
- Use WebSocket or event-driven updates
- Audit polling intervals and cache invalidation logic
- Log at the edge to catch hidden noise
3. Over-Reliance on Object-Mapping Frameworks
Entity Framework, Hibernate, or AutoMapper can hide brutal inefficiencies—until they're your bottleneck.
Symptoms:
- N+1 query issues
- Objects with 50 fields when you only need 3
- Huge memory allocation spikes on simple queries
Fix it:
- Use raw SQL or targeted DTOs for perf-critical paths
- Benchmark ORM vs manual execution
- Never let "convenience" own your hot path
4. Forgotten Test Data Growth
That test data you seeded in 2020? It’s now 1.2M records, all being loaded in every test run.
Symptoms:
- Gradually slower test pipelines
- Increased memory pressure during builds
- Flaky tests with unexplained timeouts
Fix it:
- Prune your datasets
- Use environment-aware seeding
- Don’t replicate production chaos in every test
5. Single-Threaded Background Workers
One thread processing a backlog of jobs because "that was good enough for dev."
Symptoms:
- Latency spikes during batch operations
- Missed SLAs under minor load
- High CPU on a single core
Fix it:
- Parallelize the workload where safe
- Use queue depth monitoring
- Scale horizontally, not just vertically
6. Garbage Collection Choke Points
You don’t have a memory leak. You have a memory traffic jam.
Symptoms:
- Random latency spikes
- High GC pause times on Gen2 collections
- Sudden throughput collapse with no code changes
Fix it:
- Analyze allocation rates (not just heap size)
- Use Span or pooled allocators where appropriate
- Tune GC settings for your real workload, not defaults
7. Tooling Blindness (Bad Metrics = False Confidence)
You're monitoring the wrong thing well, and missing the right thing entirely.
Symptoms:
- 99th percentile looks great, but users are still complaining
- CPU and memory "look fine"
- Logging without context, or metrics without structure
Fix it:
- Use RED or USE metrics models
- Profile before you instrument
- Correlate logs, traces, and metrics into a single signal
Final Word
Performance problems rarely start as outages. They begin as small leaks in attention, rigor, and curiosity.
Run your current system through this list. Find one? You’re sitting on a fuse.
Want help debugging your system or setting up a teardown stack? steve@steveleggett.dev