Prompt and context
A real-time collaboration service has tens of thousands of long-lived connections using a third-party WebSocket library. Node.js 22.4 marks WebSocket stable, and the team wants fewer dependencies. Cover compatibility, lifecycle, backpressure, authentication, observability, and rollback.
What the interviewer tests
They are checking whether you distinguish API stability from production maturity, and whether you can design limits, heartbeats, broadcast backpressure, graceful shutdown, and a measured migration.
Questions to clarify
Ask about client protocol, proxy support, message size, peak connections, credential renewal, cross-node fan-out, reconnects, and regional failure. Identify extension behavior and guarantees that the current library must retain.
30-second answer
“I would build an API and protocol difference matrix and verify Node 22.4+, proxies, and clients. Each connection gets auth, heartbeat, idle, and message-size limits; broadcasts use bounded queues for backpressure, and shutdown drains after refusing new connections. I would shadow and canary, compare handshake success, disconnects, P95 latency, memory, and CPU, and keep a switch to the third-party implementation for fast rollback.”
Step-by-step deep dive
Verify runtime and protocol
Node documentation says WebSocket is no longer experimental from v22.4.0. Lock the Node version and verify handshakes, extensions, proxy timeouts, and TLS settings.
Design connection lifecycle
Authenticate and authorize at connect time, then set idle, heartbeat, and close-code policies. During restart, stop accepting connections, drain, and send a reconnect hint.
Handle backpressure and size
Give each connection a bounded send queue. At the limit, drop rebuildable messages, degrade, or disconnect slow consumers. Cap frames and aggregate messages so one client cannot exhaust memory or the event loop.
Enforce authentication and authorization
Validate a short-lived credential during the handshake and recheck tenant and resource permissions for sensitive messages. Failed renewal closes the connection and requires re-authentication.
Scale across nodes
Keep connection state local and route events through a message bus. Give subscriptions a version or cursor so reconnects can replay gaps rather than broadcast everything again.
Canary, monitor, and roll back
Enable internally and for 1% of traffic. Compare handshake failures, connection survival, reconnects, queue drops, P95 latency, memory, and CPU. Keep the old implementation switchable and revert on protocol or resource regressions.
Model answer
I would not replace the library just because the API is stable. I would lock Node 22.4+, proxy, and client compatibility, then add handshake auth, heartbeat, idle timeout, size caps, and bounded send queues. Nodes share events and cursors, not connection state, and reconnects replay gaps. A canary compares handshake success, disconnects, reconnects, queue drops, P95, memory, and CPU while the old path remains available for rollback.
Common mistakes
Equating stable API with a complete replacement
The library may provide extensions, compression, or reconnect behavior. Document and test each difference.
Omitting a slow-consumer policy
Unbounded queues exhaust memory. Bound the queue, drop rebuildable messages, or disconnect.
Authorizing only at handshake
Permissions can be revoked during a long connection. Sensitive actions need message-level checks or a policy version.
Killing connections during restart
That causes a reconnect storm. Drain, jitter reconnects, and use explicit close codes.
Follow-up questions
How do you control a reconnect storm?
Return a clear close reason, use exponential backoff with jitter in clients, and rate-limit by IP, tenant, and account on the server.
When would you keep the third-party library?
Keep it when required extensions, compression, proxy compatibility, or mature telemetry are not matched and replacement value is low; record the evaluation boundary.
How do you test backpressure?
Inject slow clients and burst broadcasts, then observe queue limits, drop behavior, event-loop delay, and memory curves.
How do you preserve ordering across nodes?
Assign a monotonic cursor per subscription stream and carry versions through the bus; clients detect gaps and request replay.