SYSTEM DESIGN · PERFORMANCE · ~8 MIN READ

Improper
Instantiation

A real conversation about the antipattern of recreating expensive objects — like HttpClient — on every request, and why that can exhaust sockets and tank an application's throughput.

Improper Instantiation diagram: many requests creating HttpClient instances and too many sockets, contrasted with one reused instance with lower cost.
IMPROPER INSTANTIATION · WHEN NEW PER REQUEST BECOMES COST

QUESTION 01

MARCELO

Summarize that article for me — what is this "Improper Instantiation" thing?

THE ANSWER

It's the architecture antipattern of repeatedly creating expensive objects, or objects that were designed to be reused. The core idea is simple: if an object was built to be shared, constantly creating and destroying new instances of it on every request can wreck an application's performance.

The classic example is HttpClient. This code looks innocent:

C#LOOKS INNOCENT
using (var httpClient = new HttpClient())
{
    return await httpClient.GetStringAsync(url);
}

But in a web application, this creates a brand-new HttpClient on every request. Under heavy load, the server can exhaust sockets and start throwing SocketException.

The same applies to any object that's expensive to initialize, even without sockets involved:

BAD PATTERNEVERY REQUEST
Request
   ↓
new ExpensiveService()
   ↓
configure/connect/initialize
   ↓
use
   ↓
discard

Paying that cost again on every request reduces throughput and increases latency. The fix is to reuse the instance when the object is shareable and thread-safe:

GOOD PATTERNONCE
App starts
      ↓
creates HttpClient ONCE
      ↓
 ┌────┼────┐
 ↓    ↓    ↓
Req1 Req2 Req3
 └────┼────┘
      ↓
same instance

Depending on the resource, the fix can be a singleton or a pool of reusable objects. But there's an important caveat: this isn't "turn everything into a singleton". The antipattern only exists when you recreate something that could be shared — and the object needs to be safe for concurrent use. Scarce resources, like database connections, usually make more sense with pooling than with one instance kept alive forever.

  • Symptoms: dropping throughput and rising error rates under load.
  • Symptoms: exhausted sockets, connections, or file handles.
  • Symptoms: higher memory use and more garbage collection activity.
  • Symptoms: unexplained spikes in network, disk, or database activity.
CriterionRecreated per requestReused
Breaking point~30 concurrent usersnot reached in the test
Sustained loaddegrades with errors~500 req/s, no errors
Average response timebaselineroughly cut in half
MENTAL MODEL FOR SYSTEM DESIGN

Expensive object + used frequently + thread-safe → think reuse or pooling, not new on every request.

QUESTION 02

MARCELO

Okay, but what's the advantage of destroying the HttpClient all the time? Isn't it basically a portal for talking to another server?

THE ANSWER

Exactly — you can think of HttpClient as a channel, a portal your code uses to make HTTP requests to other servers. And destroying that channel all the time usually brings no advantage at all.

The article isn't really about "server 1 talking to server 2" — it's about a programming mistake: creating expensive communication objects repeatedly when they could be reused.

YOUR BACKENDCALLING ANOTHER SERVER
marcelomurilo.com (backend)
        |
        | HttpClient
        v
api.openai.com

The problem the article describes is doing this under load:

WRONGONE CHANNEL PER REQUEST
Request 1 arrives → creates HttpClient → calls external API → destroys it
Request 2 arrives → creates ANOTHER      → calls              → destroys it
Request 3 arrives → creates ANOTHER      → calls              → destroys it

The recommended approach looks more like this — the same channel making several calls:

RIGHTONE CHANNEL, MANY CALLS
marcelomurilo.com (backend)
          |
          v
     HttpClient
     single / reused
      /        \
     v          v
OpenAI API   GitHub API

Worth untangling a common confusion here: routes like /conteudos and /en/conteudos aren't "separate servers", each with its own HttpClient. In practice they're just different routes on the same site:

ROUTESSAME SITE
marcelomurilo.com
   |
   ├── /conteudos
   └── /en/conteudos

HttpClient only enters the picture if one of those routes needs to fetch data from another service:

FLOWWHERE HTTPCLIENT FITS
User
  ↓
marcelomurilo.com/conteudos
  ↓
your backend
  ↓
HttpClient
  ↓
external API

In one sentence: when your program needs an expensive object like HttpClient to reach other services, don't create a new one per request — reuse it whenever it's safe to do so.

QUESTION 03

MARCELO

What is socket exhaustion, exactly? What's a socket here?

THE ANSWER

In networking, a socket is basically an endpoint of communication between two computers or processes.

Illustration of the socket is not a sock joke: a tennis sock is separated by an X from two servers connected through a network socket.
SOCKET ≠ SOCK · IN THIS ARTICLE, IT MEANS NETWORK
NETWORKSOCKET
Your server
   |
   | socket
   v
Another server

When your backend makes an HTTP call, the operating system uses sockets under the hood to maintain that network communication. And there are limits: how many sockets or connections can stay open, or sit in certain states, at the same time.

If the code creates and destroys HttpClient too often, it ends up opening too many connections in sequence. The system runs out of available resources and you start seeing errors like SocketException — exactly one of the symptoms the article highlights.

QUESTION 04

MARCELO

Got it. So if I don't destroy the HttpClient connection, what are the trade-offs?

THE ANSWER

There are trade-offs, yes. Sharing a single instance requires it to be thread-safe. HttpClient was designed for that kind of use, but not every object is.

You can also run into trouble if you mutate global properties on that shared HttpClient while multiple requests are happening at once — changing DefaultRequestHeaders on every request, for example, can cause a race condition.

And not every resource should be kept forever. The article itself points to database connections: holding one open without a real need can stop other users from being able to use the database. In those cases, pooling usually makes more sense than a single, permanently held instance.

So the rule isn't "never destroy anything". It's closer to this:

Trade-offs diagram: cheap objects can be recreated, expensive and thread-safe objects should be reused, and limited resources should use a pool.
TRADE-OFFS · RECREATE, REUSE, OR USE A POOL
  • Cheap, disposable object → create and destroy it freely.
  • Expensive, shareable, thread-safe object → reuse the same instance.
  • Scarce, concurrent resource (e.g. a database connection) → consider a pool.

QUESTION 05

MARCELO

So what the article describes was really just a programming habit?

THE ANSWER

Yes — Improper Instantiation is essentially about a common habit. The instinct usually goes: "I created the object, I used it, I don't need it anymore, so I'll destroy it to free memory." That normally sounds like good practice.

Except for objects like HttpClient, which wrap expensive resources and external connections, doing create → use → destroy a thousand times can be much worse than create once → use a thousand times.

And one important conceptual correction: HttpClient isn't exactly "the connection" itself. It's more of a manager for HTTP communication that, internally, can open and reuse connections and sockets:

HIERARCHYTHE MISSING PIECE
HttpClient
   ↓
manages HTTP requests
   ↓
connections
   ↓
sockets
   ↓
network

That management layer — and the reuse it enables — is exactly what the original article wants you to preserve.

END · THANKS FOR READING

Expensive, reusable object → don't recreate it per request.

Improper Instantiation means burning CPU, memory, connections, and time recreating objects that could simply be reused. Keep the mental model: an object that's expensive, used often, and thread-safe calls for reuse or pooling — not a new on every request.

READ BACK PRESSURE BACK TO CONTENT