Why RPC, and Why gRPC
REST won the public web, but inside a system — between the dozens of services that make up a modern backend — a different model dominates: RPC. Instead of modeling everything as resources and verbs over HTTP, you call a remote function as if it were local. gRPC is the modern, high-performance realization of that idea, built on Protocol Buffers and HTTP/2. Understanding why it exists explains when to reach for it over REST.
Most engineers meet REST first and assume it’s the only way services talk. It isn’t. For internal, service-to-service communication — the chatty, high-volume traffic between your own backend components — remote procedure calls are often the better fit, and gRPC is the framework that made RPC fast, typed, and pleasant. This series builds gRPC and Protocol Buffers from the ground up. This first post is about the why: what problem RPC solves and what gRPC brings to it.
The RPC idea
Remote procedure call is a decades-old idea with a simple pitch: calling a function on another machine should feel like calling one locally. You write user := client.GetUser(ctx, &GetUserRequest{Id: 42}) and it looks and reads like an ordinary method call — but under the hood the arguments are serialized, sent over the network, executed on a remote server, and the result is sent back and deserialized into a return value.
The point is abstraction. All the mechanics — serialization, framing, transport, error propagation — are hidden behind a generated function signature. You think in terms of operations (“get this user,” “charge this card,” “stream these events”), not in terms of HTTP methods, URL paths, status codes, and JSON shapes you assemble by hand. For internal APIs where both sides are yours, that operation-centric model often maps far more naturally to what you’re actually doing than REST’s resource-centric one.
What gRPC is
gRPC is Google’s open-source RPC framework, and it’s really the combination of three well-chosen pieces:
- Protocol Buffers as the interface definition language and the wire format. You define your services and messages in a
.protofile, and that file is the single source of truth for both sides. - HTTP/2 as the transport, which brings multiplexing, streaming, header compression, and long-lived connections (the subject of a later post).
- Code generation that turns the
.protointo typed client and server stubs in your language, so you call methods instead of assembling requests.
The result is a system where you write an interface once, generate strongly-typed code for every language you use, and get efficient binary communication for free. The .proto contract is the heart of it — everything else is generated from it.
Where gRPC beats REST
gRPC’s advantages come directly from those three pieces, and they matter most for internal, high-traffic APIs:
- Performance. Protocol Buffers serialize to a compact binary format, far smaller and faster to encode/decode than JSON text. Over HTTP/2’s efficient, multiplexed connections, this means lower latency and higher throughput — significant when services exchange millions of messages.
- A strict, typed contract. The
.protofile defines exactly what every message and method looks like. Both client and server generate code from it, so a mismatch is a compile-time error, not a 3 a.m. production surprise from a mistyped JSON field. The contract is enforced, not merely documented. - Streaming as a first-class citizen. Thanks to HTTP/2, gRPC natively supports not just request→response but streaming in either or both directions — a server streaming many results, a client streaming many inputs, or a full bidirectional conversation. Doing this over plain REST is awkward; in gRPC it’s built in (post 3).
- Polyglot by design. Generate a Go server and Python, Java, and TypeScript clients from the same
.proto, all guaranteed to agree. This is why gRPC thrives in polyglot microservice environments.
Where REST still wins
gRPC is not a universal replacement, and pretending otherwise leads to using it in the wrong places. REST remains the better choice when:
- The client is a browser or the public internet. Browsers can’t speak raw gRPC directly (it needs HTTP/2 framing they don’t fully expose to JavaScript); public APIs benefit from REST’s ubiquity, cacheability, and human-readability. gRPC-Web exists to bridge browsers, but for a public-facing API, REST/JSON is usually the pragmatic default.
- Human readability and debuggability matter most. JSON you can read in a browser or
curlby hand; Protocol Buffers are binary and need tooling to inspect. For an API meant to be explored by third parties, that friction counts. - You want to lean on HTTP’s ecosystem — caching, standard status codes, and the vast tooling built around REST.
The clean rule of thumb: gRPC for internal service-to-service traffic where performance, typed contracts, and streaming matter; REST for public, browser-facing, or human-explored APIs. Many systems use both — gRPC between internal services, a REST/JSON gateway at the public edge.
The mental model for the series
Everything in gRPC flows from one artifact: the .proto file. It defines your messages (the data structures) and your services (the callable methods), and from it the toolchain generates the typed stubs both sides use. Master that contract and its generated code, and the rest — the four call types, deadlines, errors, streaming, production concerns — are layers on top.
So the series proceeds bottom-up: Protocol Buffers and the wire format (post 2), the four kinds of RPC (post 3), how code generation and stubs work (post 4), deadlines/metadata/interceptors (post 5), error handling (post 6), streaming and backpressure in depth (post 7), and running gRPC in production (post 8). By the end, RPC will feel like what it’s meant to be — calling a function that happens to live on another machine — with none of the mechanics hidden from your understanding.
Key takeaways
- RPC models remote communication as calling a function: you invoke an operation and the framework hides serialization, transport, and error propagation — an operation-centric model that often fits internal APIs better than REST’s resource-centric one.
- gRPC is the combination of three pieces: Protocol Buffers (the IDL and binary wire format), HTTP/2 (multiplexed, streaming transport), and code generation (typed client/server stubs) — all driven by a single
.protocontract. - gRPC beats REST for internal APIs on performance (compact binary over HTTP/2), a strict typed contract (mismatches are compile-time errors), first-class streaming, and polyglot codegen from one definition.
- REST still wins for browsers/public APIs, human readability and easy debugging, and leaning on HTTP’s caching/tooling ecosystem — gRPC can’t be spoken directly by browsers without gRPC-Web.
- The rule of thumb: gRPC for internal service-to-service traffic; REST for public/browser/human-facing APIs — and many systems use both, with a REST gateway at the edge.