Skip to content
Let's talk →
← Insights
Architecture

Polling or WebSockets? How I decide whether an existing architecture should change

I don’t start a review by asking whether I would build the system the same way today. I start by understanding what problem it was originally designed to solve — and whether that problem is still relevant.

September 20268 min readArchitecture reviewRostyslav Yeromenko
Original requirement
Each location operates autonomously
Location A
PC
PC
PC
Local server
Location B
PC
PC
Local server
CLOUD
Business changes
multiplayer
real-time data
device growth
New requirement
Direct two-way communication
PC
PC
PC
PC
Central service
Requirement A produced architecture A. The requirement changed, so the architecture changed. Not: bad architecture replaced by good architecture.

I had a good example of this in a system used across multiple physical locations. Each location had its own local server. Gaming computers inside the location continuously polled that server and sent data to it. The local server aggregated information and communicated with a central cloud server.

SimplifiedComputer → local server → cloud

At first glance, there were several things I didn’t like. There was a server that had to be deployed and maintained at every location. Logic existed on several levels. Clients were continuously polling the local server, and the whole communication flow was more complicated than I thought it needed to be.

But that doesn’t mean the original architecture was wrong.

The original architecture solved a real problem

The system had originally been designed around an important requirement: each location should be able to operate autonomously. The local server made that possible. Even without a permanent connection to the central cloud environment, the computers at that location still had local infrastructure they could communicate with.

So I don’t think it would be fair to look at the architecture several years later and simply say: polling is bad, local servers are unnecessary, this should use WebSockets. The original developers were solving a different problem.

What actually triggered the change
The architecture became a problem when the business requirements changed — multiplayer between locations, better real-time statistics, and a planned increase in devices and load. The autonomy that had been a strength was now working against the new goals.

Polling itself wasn’t the problem

I would still deliberately use polling today in the right situation. If the server doesn’t need to proactively send information to a client, and the client only needs to periodically retrieve or report status, polling can be a perfectly reasonable solution. It is simple and easy to understand, and sometimes a status update every few seconds or minutes is all the product actually needs.

In our case, however, communication needed to become genuinely bidirectional. The computers were sending information to the server, but the server also needed to send commands back to specific computers.

Polling
anything for me?
no
anything for me?
no
anything for me?
no
anything for me?
yes: command
Fine when the server never needs to initiate. Most answers are “no”.
Persistent connection
connection open
connection open
connection open
command for PC-14
acknowledged
connection open
connection open
command for PC-3
The server sends the command when the command actually exists.
Neither pattern is better in general. They answer different questions about who needs to initiate.

The important question was not “which technology is better?”

The question was: what kind of communication does the system actually need now? If we only needed periodic status updates, I would probably have kept the simpler model. But the new business functionality required two-way real-time communication between specific computers and the server. That made WebSockets a logical choice in this particular case.

This distinction matters because architectural decisions are often discussed as if technologies formed a hierarchy.

REST → events
polling → WebSockets
monolith → microservices
I don’t think architecture works like a hierarchy. Every additional mechanism has a cost. The useful question is whether that cost solves a real requirement.
Rostyslav Yeromenko — Engineering

Future scale matters, but I don’t design for abstract “100× growth”

There was another reason to make the change before the system had actually failed. Management had concrete plans to increase the number of locations and devices, so this wasn’t a theoretical discussion. We already knew the direction the business wanted to take. I prefer that kind of information to statements like “we need something that can scale 100×”. I would rather know:

1
How many locations exist today, and how many are expected? – with a date attached.
2
How many devices will each location have? – the per-site load, not just the total.
3
What communication needs to happen between them? – who initiates, and how quickly.
4
How expensive is downtime? – what it costs the business per hour, in real terms.
5
Are there network-quality constraints at the locations? – connectivity decides what is even possible.

Those are architectural requirements I can work with. An abstract requirement to “make it scalable” can justify almost any amount of unnecessary engineering.

Migration complexity also belongs in the decision

Moving away from the existing model required changing a significant part of the system, so there was a real migration cost. But there was another interesting result: once the migration was complete, the new architecture was easier to understand and maintain than the previous two-level model with local servers.

Two different kinds of complexity
Migration complexity
One-time cost, paid once and finished.
Operational complexity
Paid every year the system runs.
Sometimes a technically attractive architecture reduces one problem but leaves the company operating something much more complicated for the next five years.

In this case, we accepted a significant one-time change in exchange for a simpler long-term model that also supported the new requirements.

We validated the change according to the actual risk

We didn’t have hundreds of locations at the time, so I didn’t think we needed an elaborate multi-stage production rollout simply because that is what a theoretical deployment playbook might recommend. We first tested the new communication model locally on a limited number of computers, checked that connections remained stable and that we weren’t seeing errors or losing data. Once the system behaved reliably, we deployed the change across the existing locations.

We also didn’t collect extensive before-and-after benchmarks. The existing load was still relatively low, so such numbers would not have told us very much. If the system had already been operating under significant load, I would have wanted objective measurements — request rate, latency and connection stability — before making conclusions. The validation method should match the actual situation.

Best practices are a starting point

I treat architectural best practices as accumulated experience, not as rules that replace thinking. Someone has seen a pattern create problems repeatedly and turned that experience into a recommendation. That’s useful. But the recommendation doesn’t know anything about the particular system I’m looking at.

In this case, “polling is bad, use events instead” would have been too simplistic. The original architecture solved the autonomy requirement it was designed for, and replacing it without understanding that context would have been a mistake. But keeping it simply because it had worked for years would also have been a mistake once the requirements changed.

My rule for existing architecture

Before suggesting major changes
Why was it built this way?
Does that requirement still exist?
What does the business need from it now — and next?
Does the current architecture support it?
YES
Leave it alone
An imperfect architecture that meets current and expected scale is fine.
NO
Weigh cost against risk
Migration cost, one-time versus permanent complexity, then decide whether to change.
If the replacement gives no meaningful advantage at current and expected scale, and migration cost is high, I’m comfortable leaving an imperfect architecture alone.

Not every old decision needs correcting. But when a concrete business requirement makes the limitation predictable, I would rather address it proactively than wait for downtime, data loss or operational problems to prove something we could already see coming. Understand why the system behaves the way it does before changing it.

Rostyslav Yeromenko
Written by
Rostyslav Yeromenko
Founder & Managing Director — engineering and architecture
More about how we work →
Wondering whether your architecture still fits?

Tell us what the system was built for and where the business is going. We’ll say whether a change is worth the migration cost.

Request a review →
Skip to content
← InsightsArchitecture

Polling or WebSockets? How I decide whether an existing architecture should change

I don’t start a review by asking whether I would build the system the same way today. I start by understanding what problem it was originally designed to solve — and whether that problem is still relevant.

Rostyslav Yeromenko
September 2026 · 8 min read · Architecture review
Original requirement
Each location operates autonomously
Location A
PCPCPC
Local server
Location B
PCPC
Local server
CLOUD
Business changes
multiplayerreal-time datadevice growth
New requirement
Direct two-way communication
PC
PC
PC
PC
Central service
Requirement A produced architecture A. The requirement changed, so the architecture changed. Not: bad architecture replaced by good architecture.

I had a good example of this in a system used across multiple physical locations. Each location had its own local server. Gaming computers inside the location continuously polled that server and sent data to it. The local server aggregated information and communicated with a central cloud server.

SimplifiedComputer → local server → cloud

At first glance, there were several things I didn’t like. There was a server that had to be deployed and maintained at every location. Logic existed on several levels. Clients were continuously polling the local server, and the whole communication flow was more complicated than I thought it needed to be.

But that doesn’t mean the original architecture was wrong.

The original architecture solved a real problem

The system had originally been designed around an important requirement: each location should be able to operate autonomously. The local server made that possible. Even without a permanent connection to the central cloud environment, the computers at that location still had local infrastructure they could communicate with.

So I don’t think it would be fair to look at the architecture several years later and simply say: polling is bad, local servers are unnecessary, this should use WebSockets. The original developers were solving a different problem.

What actually triggered the change
The architecture became a problem when the business requirements changed — multiplayer between locations, better real-time statistics, and a planned increase in devices and load. The autonomy that had been a strength was now working against the new goals.

Polling itself wasn’t the problem

I would still deliberately use polling today in the right situation. If the server doesn’t need to proactively send information to a client, and the client only needs to periodically retrieve or report status, polling can be a perfectly reasonable solution. It is simple and easy to understand, and sometimes a status update every few seconds or minutes is all the product actually needs.

In our case, however, communication needed to become genuinely bidirectional. The computers were sending information to the server, but the server also needed to send commands back to specific computers.

Polling
anything for me?
no
anything for me?
no
anything for me?
no
anything for me?
yes: command
Fine when the server never needs to initiate. Most answers are “no”.
Persistent connection
connection open
connection open
connection open
command for PC-14
acknowledged
connection open
connection open
command for PC-3
The server sends the command when the command actually exists.
Neither pattern is better in general. They answer different questions about who needs to initiate.

The important question was not “which technology is better?”

The question was: what kind of communication does the system actually need now? If we only needed periodic status updates, I would probably have kept the simpler model. But the new business functionality required two-way real-time communication between specific computers and the server. That made WebSockets a logical choice in this particular case.

This distinction matters because architectural decisions are often discussed as if technologies formed a hierarchy.

REST → events
polling → WebSockets
monolith → microservices
I don’t think architecture works like a hierarchy. Every additional mechanism has a cost. The useful question is whether that cost solves a real requirement.
Rostyslav Yeromenko — Engineering

Future scale matters, but I don’t design for abstract “100× growth”

There was another reason to make the change before the system had actually failed. Management had concrete plans to increase the number of locations and devices, so this wasn’t a theoretical discussion. We already knew the direction the business wanted to take. I prefer that kind of information to statements like “we need something that can scale 100×”. I would rather know:

1
How many locations exist today, and how many are expected? – with a date attached.
2
How many devices will each location have? – the per-site load, not just the total.
3
What communication needs to happen between them? – who initiates, and how quickly.
4
How expensive is downtime? – what it costs the business per hour, in real terms.
5
Are there network-quality constraints at the locations? – connectivity decides what is even possible.

Those are architectural requirements I can work with. An abstract requirement to “make it scalable” can justify almost any amount of unnecessary engineering.

Migration complexity also belongs in the decision

Moving away from the existing model required changing a significant part of the system, so there was a real migration cost. But there was another interesting result: once the migration was complete, the new architecture was easier to understand and maintain than the previous two-level model with local servers.

Two different kinds of complexity
Migration complexity
One-time cost, paid once and finished.
Operational complexity
Paid every year the system runs.
Sometimes a technically attractive architecture reduces one problem but leaves the company operating something much more complicated for the next five years.

In this case, we accepted a significant one-time change in exchange for a simpler long-term model that also supported the new requirements.

We validated the change according to the actual risk

We didn’t have hundreds of locations at the time, so I didn’t think we needed an elaborate multi-stage production rollout simply because that is what a theoretical deployment playbook might recommend. We first tested the new communication model locally on a limited number of computers, checked that connections remained stable and that we weren’t seeing errors or losing data. Once the system behaved reliably, we deployed the change across the existing locations.

We also didn’t collect extensive before-and-after benchmarks. The existing load was still relatively low, so such numbers would not have told us very much. If the system had already been operating under significant load, I would have wanted objective measurements — request rate, latency and connection stability — before making conclusions. The validation method should match the actual situation.

Best practices are a starting point

I treat architectural best practices as accumulated experience, not as rules that replace thinking. Someone has seen a pattern create problems repeatedly and turned that experience into a recommendation. That’s useful. But the recommendation doesn’t know anything about the particular system I’m looking at.

In this case, “polling is bad, use events instead” would have been too simplistic. The original architecture solved the autonomy requirement it was designed for, and replacing it without understanding that context would have been a mistake. But keeping it simply because it had worked for years would also have been a mistake once the requirements changed.

My rule for existing architecture

Before suggesting major changes
Why was it built this way?
Does that requirement still exist?
What does the business need from it now — and next?
Does the current architecture support it?
YESLeave it alone
An imperfect architecture that meets current and expected scale is fine.
NOWeigh cost against risk
Migration cost, one-time versus permanent complexity, then decide whether to change.
If the replacement gives no meaningful advantage at current and expected scale, and migration cost is high, I’m comfortable leaving an imperfect architecture alone.

Not every old decision needs correcting. But when a concrete business requirement makes the limitation predictable, I would rather address it proactively than wait for downtime, data loss or operational problems to prove something we could already see coming. Understand why the system behaves the way it does before changing it.

Rostyslav Yeromenko
Written by
Rostyslav Yeromenko
Founder & Managing Director — engineering and architecture
Wondering whether your architecture still fits?

Tell us what the system was built for and where the business is going. We’ll say whether a change is worth the migration cost.

Request a review →
Keep readingAlso: choosing the platform from the business case