You probably don't need WebSockets yet

You probably don't need WebSockets yet

April 28, 2026 · architectureperformancemvp

When founders sketch their MVP architecture and there's any "live updates" in the spec, the first thing they reach for is WebSockets.

Don't. Almost certainly don't.

WebSockets are the cool answer. They're also a stateful long-lived connection that needs a server that supports them, a reconnection strategy, message ordering guarantees, authentication on every connection, fan-out infrastructure when you have many subscribers, and a debugging story that's substantially harder than HTTP.

For most "live updates" in an MVP, polling at 5 or 30 seconds is fine. It feels real-time enough, it works on every server (including serverless functions on Vercel), and it's debuggable with the same tools you use for normal HTTP requests.

Here's how to decide which one you actually need.

The latency table

How fast does the update need to be visible? Be specific.

Use case Latency target Right answer
Chat between users <200ms WebSocket or SSE
LLM streaming (token-by-token output) <100ms per chunk SSE
Live multiplayer game state <50ms WebSocket
Notifications icon updates <30s Polling
Comments thread refresh <60s Polling
Order status (paid → shipped) <5min Polling
Dashboard KPIs <60min Page refresh / cache revalidation

The pattern: if the user expects updates within 30 seconds or less, you're probably looking at WebSockets or SSE. Above 30 seconds, polling is fine and saves you a stateful infrastructure bet.

What polling actually costs

Founders fear polling because it sounds wasteful. "Every 5 seconds, every user, you're hitting the server!"

Math it out. 100 users polling every 5 seconds = 1,200 requests/minute = 72,000/hour. Vercel's hobby tier handles 100 GB-hours/month of function execution; an unauthenticated GET that returns cached data takes ~10ms; 72,000 requests at 10ms each is 12 minutes of execution per day or 6 hours a month. You're at 6% of your limit at 100 active users.

For a real-world MVP at sub-10K MAU, polling is essentially free.

What polling is not: scale-friendly to millions of users. If you grow into that, you migrate to WebSockets or SSE. That's a real engineering effort but it's a good problem, and you'll know exactly what shape your data flow needs by then.

When to reach for WebSockets

Three actual reasons WebSockets are the right answer at MVP scale:

  1. Bidirectional real-time — both server and client need to push to each other (chat, multiplayer games, collaborative editing).
  2. High-frequency updates from server to client — a stock-ticker-like surface where new data lands every few seconds and the user is actively watching.
  3. Persistent presence — knowing who's online right now, with sub-second updates as people connect/disconnect.

If your use case isn't one of those, you don't need WebSockets.

When SSE is better than WebSockets

Server-Sent Events (SSE) are HTTP, one-way (server to client), automatically reconnect, and work on every serverless platform. They're simpler than WebSockets and cover ~80% of "real-time" use cases.

Best fits:

  • LLM streaming (tokens arriving progressively from OpenAI / Anthropic).
  • Notification feeds (server pushes new items as they happen).
  • Log tails (deploy progress, build output).

SSE is what Vercel, Cloudflare Workers, and Lambda all support natively. WebSockets on those platforms requires a separate hosted service (Pusher, Ably, AWS API Gateway WebSocket APIs).

If your real-time need is one-way (server pushes to client), use SSE. WebSockets is overkill.

The polling pattern that scales

If you're going with polling, here's the pattern that minimizes server load and feels reasonably live:

// hooks/usePoll.ts
import { useEffect, useState } from 'react';
import useSWR from 'swr';

export function usePoll<T>(url: string, intervalMs = 5000) {
  return useSWR<T>(url, (u) => fetch(u).then((r) => r.json()), {
    refreshInterval: intervalMs,
    refreshWhenHidden: false,    // pause when tab hidden
    refreshWhenOffline: false,   // pause when offline
    revalidateOnFocus: true,     // catch up when tab returns
  });
}

Three optimizations matter:

  1. Pause when the tab is hidden. SWR does this automatically. If the user has 5 tabs and one is your app, you're not polling 5 times faster.
  2. Resume when the tab gets focus. Catches the user up immediately on tab return.
  3. Cache the response on the server. A Cache-Control: max-age=5 header means even if 100 users poll the same endpoint at the same time, your server processes one request per 5 seconds and the rest hit the CDN cache.

With caching, polling load is independent of user count. 100 users polling every 5 seconds = same server load as 1 user polling every 5 seconds.

A real example

The Vibe Coder's Guide skills' chatbot (sub-skill 05) doesn't use WebSockets. It doesn't poll either. It's just a regular HTTP request — user types a message, frontend POSTs to /api/chatbot, server responds with the reply, frontend renders it.

There's no real-time concern. The user typed; the server responded; that's the whole loop. Adding WebSockets would have added complexity without any UX benefit.

If I add a "see who else is on the chatbot right now" feature later (which I won't, but hypothetically), that would be a real-time concern, and I'd start with polling at 30 seconds for the presence list. WebSockets would only enter the picture if presence latency mattered enough that 30 seconds felt wrong.

The mental model

Default to polling. Reach for SSE when you need server-pushed one-way updates. Reach for WebSockets only when you specifically need bidirectional, sub-second, high-frequency communication.

This is the same shape as most engineering rules: pick the simplest tool that works. The ROI of WebSockets is real but it's also asymmetric — you pay the complexity cost upfront, and most MVPs never reach the scale where the simpler thing breaks.

If your gut says "WebSockets," ask yourself: "what is the actual latency I need?" If the answer is anything north of 1 second, you don't need WebSockets. You need polling.

The Vibe Coder's Guide skills' data optimization skill (sub-skill 12) covers this exact decision tree. Default to the simpler thing. Upgrade when measured behavior says you should, not when intuition says it would be cool.

Did this land for you?

← All posts