---
title: "17 — Performance Antipatterns: The Mistakes That Slow Systems Down"
uid: performance-antipatterns
tags: ["noisy-neighbor", "retry-storm", "n-plus-1", "antipatterns", "roadmap:system-design", "performance", "system-design"]
excerpt: "Performance antipatterns are structural mistakes — N+1 queries, chatty I/O, no caching, retry storms — with known shapes and known fixes. Recognizing them by name is most of the battle."
date: 2026-08-13T03:27:31+0000
source: https://www.aveshina.my.id/en/blog/performance-antipatterns
---

Every performance mistake I made was a war story I learned after an incident — until they turned out to be a catalog with names. Writing them down turned the grab-bag into a reference: **performance antipatterns are common, structural mistakes (N+1 queries, chatty I/O, unbounded data, missing caches, noisy neighbors, retry storms) that lead to poor performance, and each has a known shape and a known fix.** [1] Recognizing them by name is most of the battle.

The framing that landed is that these are not random bugs — they are _patterns_, in the sense that they recur across unrelated systems because they share a root cause. N+1 queries happen whenever code loops over a collection and fetches related data per item. Chatty I/O happens whenever a logical operation is implemented as many small requests instead of one batched one. Retry storms happen whenever retry logic lacks backoff. Naming the pattern points at the fix.

## The catalog

The roadmap's antipatterns fall into a few families, and I group them that way because the fixes cluster too [1][2][3]:

```figure
<svg viewBox="0 0 740 320" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="A grid of performance antipatterns grouped into families. Data-access family: N+1 Queries, Busy Database, Extraneous Fetching. I/O family: Chatty I/O, Synchronous I/O. Resource family: No Caching, Noisy Neighbor, Improper Instantiation. Resilience family: Retry Storm, Monolithic Persistence, Busy Frontend.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- Data access family -->
    <rect x="20" y="20" width="350" height="130" rx="10" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="40" y="44" font-size="12" font-weight="700" fill="#1e1b4b">Data access</text>
    <g font-size="11" fill="#1e1b4b">
      <circle cx="44" cy="64" r="3" fill="#6366f1"/><text x="54" y="68">N+1 queries — fetch related data per item in a loop</text>
      <circle cx="44" cy="88" r="3" fill="#6366f1"/><text x="54" y="92">Busy database — too much load on one DB</text>
      <circle cx="44" cy="112" r="3" fill="#6366f1"/><text x="54" y="116">Extraneous fetching — retrieving more than needed</text>
      <circle cx="44" cy="136" r="3" fill="#6366f1"/><text x="54" y="140">Monolithic persistence — one DB for everything</text>
    </g>

    <!-- I/O family -->
    <rect x="390" y="20" width="330" height="130" rx="10" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="410" y="44" font-size="12" font-weight="700" fill="#422006">I/O &amp; resources</text>
    <g font-size="11" fill="#422006">
      <circle cx="414" cy="64" r="3" fill="#ca8a04"/><text x="424" y="68">Chatty I/O — many small requests</text>
      <circle cx="414" cy="88" r="3" fill="#ca8a04"/><text x="424" y="92">Synchronous I/O — blocking the thread</text>
      <circle cx="414" cy="112" r="3" fill="#ca8a04"/><text x="424" y="116">No caching — refetching the same data</text>
      <circle cx="414" cy="136" r="3" fill="#ca8a04"/><text x="424" y="140">Improper instantiation — needless object creation</text>
    </g>

    <!-- Resilience family -->
    <rect x="20" y="170" width="700" height="130" rx="10" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="40" y="194" font-size="12" font-weight="700" fill="#7f1d1d">Resilience &amp; capacity</text>
    <g font-size="11" fill="#7f1d1d">
      <circle cx="44" cy="214" r="3" fill="#dc2626"/><text x="54" y="218">Retry storm — unbounded retries multiplying traffic during an outage</text>
      <circle cx="44" cy="238" r="3" fill="#dc2626"/><text x="54" y="242">Noisy neighbor — one tenant monopolizing shared resources</text>
      <circle cx="44" cy="262" r="3" fill="#dc2626"/><text x="54" y="266">Busy frontend — web servers / CDN overloaded by heavy traffic or assets</text>
      <circle cx="44" cy="286" r="3" fill="#dc2626"/><text x="54" y="290">Unbounded data — processing more than the task needs</text>
    </g>
  </g>
</svg>
```

### Data-access family

- **N+1 queries.** The system makes N+1 queries to retrieve related data instead of one query with a join or a batched fetch [1]. Fetching 100 orders, then one query per order for its items, is 101 queries. The fix is almost always eager loading or a join.
- **Busy database.** The database is handling more requests than it can, leading to degraded performance, resource contention, deadlocks, and inconsistencies [4]. The fixes are the database-scaling levers from the database notes: scale out, optimize the schema, cache, index.
- **Extraneous fetching.** Retrieving more data than the task needs — selecting all columns when only two are used, fetching full objects when a count would do [5]. Leads to performance degradation and wasted network. The fix is projecting only what is needed.
- **Monolithic persistence.** Using a single database to store all data [6]. Fine at small scale; a bottleneck at large scale, limiting flexibility and scalability. The fixes are federation, sharding, or NoSQL — again, the database-scaling levers.

### I/O and resource family

- **Chatty I/O.** A large number of small I/O requests where one batched operation would do — reading database records one at a time, implementing one logical operation as a series of HTTP requests, reading a file in tiny chunks [7]. Network calls and I/O are inherently slow relative to compute, and the per-request overhead dominates. The fix is batching.
- **Synchronous I/O.** Blocking the calling thread while I/O completes [8]. The thread enters a wait state, wasting processing resources. A single synchronous I/O call can block an entire call chain. The fix is async I/O.
- **No caching.** An application handling many concurrent requests repeatedly fetches the same data from an expensive source, repeatedly constructs the same objects, or makes excessive calls to a throttled remote service [9]. The fix is one of the cache strategies from the caching notes.
- **Improper instantiation.** Creating unnecessary instances of an object, class, or service on every request when a shared instance would do [10]. The fix is reuse — singletons, object pools, connection pools.

### Resilience and capacity family

- **Retry storm.** A large number of retries in a short period, multiplying traffic and resource usage [11]. Often triggered when a downstream service slows and every upstream client retries at once. The fixes are exponential backoff, circuit breaking, and monitoring/alerting.
- **Noisy neighbor.** One component utilizes a disproportionate share of resources, causing contention for everyone else — one user hogging CPU, one process saturating I/O, one app eating bandwidth [12]. The fixes are resource isolation (the bulkhead pattern) and per-tenant quotas.
- **Busy frontend.** The user-facing layer — web servers, CDN, browser — handles more work than it can: too many concurrent users, large static assets, heavy client-side rendering, missing caches [13]. The fixes are CDNs for static files, lazy-loading and script optimization, load balancing, and fewer unnecessary API calls.
- **Unbounded data.** The system retrieves or processes more data than necessary — no pagination, no LIMIT, pulling a whole table into memory [1]. The fix is bounding the result set explicitly.

## How I use this

The value of the catalog is diagnostic speed. When a system is slow under load, I run through the families: is it data-access (N+1, busy DB, extraneous fetching)? I/O (chatty, synchronous, no cache)? Resilience (retry storm, noisy neighbor, busy frontend)? Each pattern has a signature in the metrics — N+1 shows as a query count proportional to row count; chatty I/O shows as high request count with low bytes-per-request; retry storm shows as traffic spikes correlated with downstream latency. Naming the pattern from the metric is what points me at the fix within minutes instead of hours. And the discipline of asking "which antipattern is this?" before writing code prevents most of them from shipping in the first place.

## References

[1] Microsoft, "Performance antipatterns for cloud applications," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/)

[2] Microsoft, "Busy database antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/busy-database/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/busy-database/)

[3] Microsoft, "Chatty I/O antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/)

[4] "How to avoid retry storms in distributed systems," FAUN, 2022. [Online]. Available: [https://faun.pub/how-to-avoid-retry-storms-in-distributed-systems-91bf34f43c7f](https://faun.pub/how-to-avoid-retry-storms-in-distributed-systems-91bf34f43c7f)

[5] Microsoft, "Extraneous fetching antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/extraneous-fetching/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/extraneous-fetching/)

[6] Microsoft, "Monolithic persistence antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/monolithic-persistence/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/monolithic-persistence/)

[7] Microsoft, "Improper instantiation antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/)

[8] Microsoft, "Synchronous I/O antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/synchronous-io/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/synchronous-io/)

[9] Microsoft, "No caching antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/no-caching/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/no-caching/)

[10] Microsoft, "Noisy neighbor antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/noisy-neighbor/noisy-neighbor](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/noisy-neighbor/noisy-neighbor)

[11] Microsoft, "Retry storm antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/retry-storm/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/retry-storm/)

[12] Microsoft, "Busy front end antipattern," Azure Architecture Center. [Online]. Available: [https://learn.microsoft.com/en-us/azure/architecture/antipatterns/busy-front-end/](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/busy-front-end/)

```quiz
Q: A loop fetches 50 orders, then makes one DB call per order for its line items. This is the ____ antipattern.
- N+1 queries
- chatty I/O
correct: 0
explain: N+1 is making N related-data queries (one per item) plus the original query. The fix is eager loading or a join.

Q: During a downstream outage, every client retries immediately and traffic multiplies tenfold. This is a…
- retry storm
- noisy neighbor
correct: 0
explain: Unbounded retries multiplying traffic during an outage is a retry storm. The fixes are exponential backoff and circuit breaking.

Q: One tenant on a shared server saturates the CPU, degrading performance for all other tenants. This is…
- monolithic persistence
- noisy neighbor
correct: 1
explain: One component monopolizing shared resources is the noisy-neighbor antipattern. The fix is resource isolation (bulkheads) or per-tenant quotas.

Q: The most direct fix for the "no caching" antipattern is…
- buying a bigger database server
- introducing one of the cache strategies (cache-aside, write-through, etc.) for hot data
correct: 1
explain: No caching means repeatedly fetching the same expensive data. The fix is a cache layer for the hot working set — a strategy from the caching notes.
```
