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.
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:
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:
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:
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.
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.
marcelomurilo.com (backend)
|
| HttpClient
v
api.openai.com
The problem the article describes is doing this under load:
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:
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:
marcelomurilo.com
|
├── /conteudos
└── /en/conteudos
HttpClient only enters the picture if one of those routes needs to fetch data from another service:
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.
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:
- 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:
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.