Go-Powered Developer Tools
Part 1 of 2 β’ Series Complete
A collection of high-performance developer tools and infrastructure built with Go β from real-time servers to deployment automation.


GoChat β Real-Time Chat Platform
βWebSocket chat server handling 5,000 concurrent users on a $6 VPSβ
GoChat is a production-grade real-time chat platform built with Go and WebSockets. It handles 5,000 concurrent connections at 8% CPU on a single $6/month VPS using Go's goroutine model, a channel-based Hub, and Redis pub/sub for horizontal scaling.
Tech Stack
Technologies & tools used
Frontend
1React
v18
Single-page application, native WebSocket API
Backend
2Go
v1.22
WebSocket server, Hub/Client concurrency model
gorilla/websocket
WebSocket protocol implementation in Go
Database
2Redis
v7
Pub/sub for multi-instance message routing
PostgreSQL
v16
Message history, user accounts, room persistence
DevOps
1Docker
v24
Container packaging and docker-compose local dev
Media Gallery
3 items

Chat Interface

Room Management

Load Test Results
Features & Highlights
Key capabilities and achievements
Real-Time Messaging
CoreWebSocket-based bidirectional messaging with sub-20ms delivery latency. Supports text messages, typing indicators, and read receipts.
Room Management
CoreCreate public or private rooms with shareable invite links. Room membership persisted in PostgreSQL with owner/member/admin roles.
User Presence
Online/offline/away status broadcasts to all room members within 1 second of state change. Presence state stored in Redis with 30-second TTL heartbeats.
Message History
New connections automatically receive the last 50 messages on room join. Disconnected clients can replay missed messages by sending last_received_seq on reconnect.
System Architecture
GoChat uses a three-tier architecture: a React SPA served from Cloudflare CDN, a Go WebSocket server running the Hub/Client pattern, and a PostgreSQL + Redis data layer. The Hub goroutine owns all connection state; all mutations happen via channel sends, not mutex-guarded shared memory.
Components
React Frontend
FrontendSingle-page application using native WebSocket API (no Socket.io). Maintains optimistic UI updates for sent messages and a local sequence buffer for ordering.
- WebSocket connection lifecycle management
- Optimistic UI for sent messages
- Message sequence buffer and reorder logic
- Room state and presence indicator rendering
Go WebSocket Server
BackendHandles WebSocket upgrades, runs the Hub, and acts as the Redis pub/sub bridge. Each connection spawns exactly two goroutines: a readPump and a writePump.
- WebSocket protocol handling and upgrade
- Hub-mediated message broadcast
- JWT authentication middleware
- Redis pub/sub bridge for multi-instance routing
Redis Pub/Sub
ServiceMessage broker enabling horizontal scaling. Each Hub instance subscribes to per-room Redis channels. Outbound messages are published to Redis; the local Hub delivers them to connected clients.
- Cross-instance message routing
- Presence state coordination via TTL keys
Deployment
Single VPS with Docker Compose
Challenges & Solutions
Problems solved and lessons learned
Concurrent Map Access in the Hub
TechnicalThe Hub maintains a map[*Client]bool for active connections. Multiple goroutines simultaneously register, unregister, and broadcast β classic concurrent map write panic territory.
Solution
Adopted a channel-based design: all state mutations go through the Hub's Run() goroutine via select on register/unregister/broadcast channels. Read-heavy scans use sync.RWMutex.
Outcome
Zero data races in six months of production. go test -race passes on every CI run.
Message Ordering Across Redis Hops
TechnicalWhen message A is published via Redis to instance 2 and message B is sent directly through instance 1, clients occasionally saw B before A despite B being sent later.
Solution
Added a monotonic per-room sequence number generated atomically by a Redis INCR. Clients buffer and reorder messages using a local priority queue keyed on sequence number before rendering.
Outcome
Message ordering correct in 100% of tested scenarios including simulated network partitions.
Graceful Reconnection After Mobile Network Switches
UXMobile clients frequently drop and reconnect on cellular network switches. Without state recovery, users missed messages during the disconnection window.
Solution
Implemented a last_received_seq query param on WebSocket reconnect. The server replays missed messages from PostgreSQL history before switching to live streaming.
Outcome
Reconnection is now invisible to users β the chat history simply continues without gaps.
Key Learnings
Channels are for communication, not synchronization
TechnicalEarly versions used mutexes everywhere. Refactoring to a channel-based Hub eliminated most mutex usage and made data flow dramatically easier to reason about and test.
sync.Mutex guarding every shared data structure individually
Channel-based Hub as the single authority for connection state
Now evaluate every mutex use β in most cases a dedicated goroutine with channels is cleaner
Profile before optimizing
ProcessThe actual bottleneck was NOT WebSocket I/O β it was string allocation in the JSON marshaling hot path.
Assumed WebSocket I/O was the bottleneck, tried buffering tweaks
Used pprof CPU profiles to identify JSON marshaling as the hot path
pprof is now standard in every Go service I build
Design for horizontal scaling from day one
TechnicalAdding Redis pub/sub as an afterthought required significant Hub refactoring.
Single Hub instance with in-memory broadcast only
Hub interface with swappable backend (memory for tests, Redis for production)
Always implement the multi-instance abstraction even on single-instance deployments
Key Takeaways
- β’Go goroutines are cheap enough to use one per connection even at scale
- β’Channel-based Hub beats mutex maps for concurrent connection management
- β’Profile first β the bottleneck is almost never where you expect
- β’Build the horizontal scaling abstraction on day one
Related Content
Explore related articles, projects, and tools.