gRPC
Tool
A communication standard that lets one service call a function on a distant service as if it were local, used where services inside a company talk to each other constantly.
Why it exists
As microservices — splitting a system into small services by feature — spread, the number of calls between servers climbed. Function calls that used to happen inside a single deployable became requests crossing a network, and rendering one screen could involve half a dozen services calling each other.
The trouble was that the tools used for those calls were designed for traffic between a browser and a server. Hitting a URL and exchanging JSON is convenient because a person can read the result, but in a conversation between two servers nobody ever reads, that convenience earns nothing.
The other problem was that the contract lived only in a document. Theo at Pagedeck was writing code to call the permissions service and opened the internal wiki to check the field names in the response, only to find a page that had stopped being updated three months earlier. He sent a request by hand, read the fields off the response, and wrote the code against that. The following week the permissions service renamed a field, and the document service found out from a user-facing screen after the deploy was already finished.
gRPC aims at both of those. It pins the contract in a file machines read rather than a document people read, and it swaps the data for a much smaller format that people can't read.
What it solves
Two problems, contracts that drift and round trips that pile up, handled four ways.
- The contract gets pinned to one file. A definition file states which functions exist and what each one takes and returns, and the code on both sides is generated from that file. Nobody hunts for field names in a wiki, and when the permissions service renames a field the document service fails at build time.
- The data gets smaller. Instead of shipping field names as text on every call, only the numbers assigned in the definition file go over the wire. A request carrying a single user id runs about twenty bytes as text and five or six bytes here.
- Connections get reused. Rather than opening 100 connections for 100 requests, many requests ride over one connection at the same time. Opening a fresh connection often costs more time than the request itself, so this alone shortens the round trip.
- Results can arrive as a stream. Besides one call and one response, the standard covers one call whose results keep flowing back, which fits uploading a file in pieces or watching progress as it happens.
How much any of that is worth depends on the call count. The Pagedeck document service calls the permissions service 40 times to draw one list screen. Cutting a round trip from 12 milliseconds to 4 turns 480 milliseconds into 160, and that is a difference users feel. On an endpoint called 100 times a day, the same 8 milliseconds means nothing.
Alternatives
Four approaches sit in the same space, and the thing that picks between them is who does the calling.
- REST — every piece of data gets a URL, and you call the URL to read or write. Browsers can call it directly and the tooling is everywhere, so public endpoints are usually built this way.
- GraphQL — the caller lists the fields it needs and gets back exactly those. Five calls to render one screen collapse into one, at the cost of more setup on the side that serves it.
- WebSocket — the connection stays open and either side can speak at any time. It fits places where one side's change has to appear on the other immediately, such as two people editing the same document.
- Webhook — the direction flips. Instead of the caller asking repeatedly, the side where the event happened posts to the other's address first.
If the caller is a browser, gRPC drops out of the running, because browsers can't speak the protocol directly and a translation layer has to sit in between. Public endpoints are the same story: whoever integrates needs to try a call with familiar tools, and a format people can't read raises that bar.
There are costs on the other side too. You can't fire off a request with ordinary command line tools and have to install dedicated ones, and the team needs a rule about who edits the definition file. Pagedeck keeping its public endpoints on REST and converting only internal service calls is a decision to pay that cost in one place.
How it sounds at work
"This one is internal, so let's use gRPC. Round trips are stacking up on a single screen."
The caller isn't a browser and calls are frequent enough that shortening the round trip is worth something.
"Push the definition file first and we'll generate our side from it."
A request to work from one file instead of passing field names around in documents.
"It's gRPC, so you can't poke it from a browser. You'll need to install the tool."
Debugging works differently once you move off plain text over URLs.
Pagedeck moved to the protocol after complaints piled up about a slow document list screen. Theo measured it and found that 480 of the 800 milliseconds spent drawing the screen went to 40 trips to the permissions service. Cutting the number of calls was considered, but permissions differ per document, so they were hard to batch.
Most of the time the migration took went into writing the definition file. Putting every field the two services exchanged in one place for the first time surfaced six fields nobody was reading.
What people get wrong
The most common reading is that gRPC is always faster than REST. Benchmarks placed side by side show gaps of several times over, which makes that easy to believe. The gap attaches to a single round trip, though, so it is only felt where round trips are frequent. Saving 8 milliseconds on an endpoint called 100 times a day adds up to less than a second. Left uncorrected, this belief produces public endpoints built on gRPC, and the partner who wanted to integrate walks away without managing a single test call.
Assuming a definition file makes broken contracts impossible is nearly as common. A few build failures caught early make the file feel like full protection. In practice, reusing a field number or changing a field's type is a breaking change, a change that breaks the code of whoever was already calling you. The new code runs fine while a service that hasn't migrated reads a nonsense value, so it also surfaces late. Retiring a field means reserving its number so it never gets reused, which is how backward compatibility survives.
Where to go next
Last updated 2026-09-16