Every backend system moves data between machines. You pick the wrong tool for that job, and the failure shows up months later, under load, at the worst possible time. TCP, Redis, and gRPC solve different problems. Confuse them, and you build a system that looks fine in a demo and falls apart in production.
This article walks through what each piece does, where each one sits in your stack, and how a single wrong choice among these three breaks a real system.
What TCP actually does
TCP sits at the transport layer. Every HTTP request, every database connection, every gRPC call rides on top of TCP. TCP guarantees three things: your bytes arrive, they arrive in order, and the connection tells you when something drops.
TCP does not know about your application. It does not know what a "user" is or what a "message" means. It moves bytes and guarantees delivery order. Building your own protocol directly on raw TCP means you write your own framing, your own retry logic, and your own error handling. Most teams underestimate this work.
Raw TCP sockets make sense in a narrow set of cases: game servers that need custom binary protocols, low-level infrastructure tools, or systems where HTTP overhead costs too much on a tight latency budget. For almost everything else, you want a protocol built on top of TCP, not TCP by itself.
The mistake here: engineers open raw sockets for a job that needed HTTP or gRPC, then spend weeks rebuilding features those protocols give you free. Retries, timeouts, and structured payloads do not exist by default on a bare socket. You add them yourself, and you add the bugs that come with rebuilding networking primitives from scratch.
What Redis actually does
Redis is an in-memory data store. Data sits in RAM, so reads and writes finish in under a millisecond in most setups. Redis is not a message queue by design, though people use it as one. Redis is not a primary database, though people use it as one of those too.
Redis earns its place in four spots.
First, caching. You store the result of an expensive database query or API call so the next request skips the work entirely.
Second, session storage. A user logs in on one server. The next request lands on a different server behind your load balancer. Redis holds the session so any server in your fleet can read it.
Third, rate limiting and counters. Redis increments and expires keys fast enough to gate requests per user, per second, without slowing your API down.
Fourth, pub or sub and lightweight queues. Services publish events, other services subscribe and react.
The trap sits in that fourth use. Redis pub or sub delivers messages only to subscribers connected at the moment of publish. A subscriber that disconnects for two seconds misses every message sent during that gap. Redis does not replay missed messages by default. Teams that treat Redis pub or sub as a guaranteed delivery queue, the way they would treat Kafka or RabbitMQ, lose data during deploys, network blips, or restarts. Nobody notices until an order goes unprocessed or a notification never fires.
Redis also loses data on a crash unless you configure persistence. Default Redis behavior favors speed over durability. That tradeoff is correct for a cache. It is wrong for anything you cannot afford to lose.
What gRPC actually does
gRPC is a framework for calling functions on a remote service as if they lived in your own process. It runs on top of HTTP/2 and uses Protocol Buffers to define the shape of every request and response.
Three properties set gRPC apart from a plain REST API.
First, the contract is typed and explicit. You define your service methods and message fields in a schema file, and both client and server generate code from that same file. A mismatched field breaks at compile time, not in production.
Second, payloads are binary, not JSON text, so they take less space on the wire and parse faster on both ends.
Third, gRPC supports streaming in both directions over one connection, so a client and server exchange a continuous flow of messages without opening a new connection for each one.
gRPC fits internal service to service traffic. Your order service calls your inventory service. Your payment service calls your fraud check service. Both sides control their code, both sides regenerate client and server stubs from the same schema, and both sides get compile time safety.
gRPC does not fit public APIs. Browsers do not support gRPC natively. A public API for third party developers needs REST or GraphQL, something a browser or a simple HTTP client reads without a code generation step. Teams that expose gRPC directly to external clients end up building a translation layer anyway, which defeats the reason they picked gRPC in the first place.
The complete flow through a real system
Picture a request moving through an order processing system.
A user submits an order from a browser. That request travels over HTTP, which itself runs on TCP. The connection handshake, packet ordering, and retransmission on packet loss all happen at the TCP layer, invisible to your application code.
Your API gateway receives the order and needs data from three internal services: inventory, pricing, and user profile. The gateway calls each one through gRPC. Each call carries a typed request message, gets a typed response back, and finishes in single digit milliseconds because the payload is compact and the connection stays open across calls.
The gateway checks Redis before hitting the pricing service, since prices for that product changed ten minutes ago and got cached then. Cache hit, no service call needed, response returns in under a millisecond.
Once the order confirms, the order service publishes an event. A notification service and an analytics service both need to react to that event. Here the choice matters again. If losing a notification is tolerable, Redis pub or sub works. If every order event must reach every subscriber even through a restart, you want a durable queue, not Redis pub or sub.
The user's session, which server they authenticated against, which cart they built, lives in Redis so any instance behind the load balancer answers their next request correctly.
Every step above depends on TCP underneath it. Redis picks up work that needs memory speed. gRPC picks up work that needs typed, fast, internal service calls. None of the three replaces the others. Each one owns a specific job.
Where the wrong choice kills the system
A team builds a payment notification pipeline on Redis pub or sub because it already runs Redis for caching, and adding a queue felt like extra infrastructure. During a routine deploy, the notification service restarts for eight seconds. Every payment confirmation published during that window disappears. Customers never get their receipt emails. Support tickets pile up. The root cause traces back to a messaging pattern that was never built to guarantee delivery.
A team builds a public API in gRPC because internal services already use gRPC and consistency sounds appealing. Their mobile app and web frontend cannot call that API directly, since browsers lack native gRPC support. They end up writing a REST wrapper around the gRPC service, doubling their maintenance surface for a decision that added no value at the edge.
A team writes a custom TCP protocol for a chat feature to save a few milliseconds over HTTP. Six months later, the team spends a full sprint fixing reconnect logic, message ordering across dropped connections, and a memory leak from unclosed sockets, problems that WebSocket or gRPC streaming already solved.
Each failure traces to the same root pattern: a tool got picked for familiarity or perceived speed, not for the guarantees the job actually needed.
How to choose without guessing
Ask what your data needs to survive. If a message must reach its destination even after a crash or restart, reach for a durable queue, not Redis pub or sub.
Ask who calls the service. Internal service to service traffic where both sides control their code fits gRPC. Public or browser facing traffic fits REST or GraphQL.
Ask whether you need raw sockets at all. Most teams do not. HTTP, gRPC, and WebSocket already solve framing, retries, and connection management on top of TCP. Building your own protocol on a bare socket only makes sense when an existing protocol's overhead genuinely costs you the latency budget you need, and you have measured that cost, not guessed at it.
Ask how fast data needs to move and how long it needs to last. Sub millisecond access with tolerance for occasional loss points to Redis. Guaranteed persistence points to a proper database or durable queue.
The pattern across TCP, Redis, and gRPC stays consistent. Match the guarantee the technology gives you to the guarantee your system actually needs. Skip that check, and the gap between what you assumed and what you got becomes an outage with your name on it.
Top comments (0)