Internal services need to talk to each other, and JSON over HTTP is the obvious default. It works, every language supports it, and you can inspect it with tools everyone already has.
It also has a quiet failure mode: the contract between two services lives in documentation and in the heads of whoever wrote them. Nothing enforces it. A field changes meaning, a type drifts, and the mismatch surfaces at runtime in production.
The contract becomes the source
With gRPC, the interface is defined in a schema file and both sides generate their code from it. The server cannot return a shape the contract does not describe, and the client cannot call a method that does not exist, because neither would compile.
This changes the review conversation. A protocol change is a visible diff in a shared file rather than a paragraph in a pull request description that the consuming team may not read.
What we actually gained
- Breaking changes caught at build time instead of at request time.
- Smaller, faster payloads on internal paths where volume is high and human readability is irrelevant.
- Streaming as a first-class capability rather than something bolted on.
- Shared types across services written in different languages, generated rather than hand-maintained.
The costs are real
It is worth being honest about what you give up.
Debugging is harder. A binary payload cannot be read from a log the way JSON can, and the tooling, while good, is not the tooling everyone already knows. New team members need to learn it.
The browser cannot speak it directly without a proxy layer, which means it is not a drop-in for public APIs consumed by web clients.
And schema files become a shared artifact that needs ownership. Without discipline about versioning and backward compatibility, you have moved the coordination problem rather than solved it.
Where we use it and where we do not
Internal, high-volume, service-to-service: gRPC. Public APIs consumed by mobile and web clients: ordinary HTTP with JSON, because reach and debuggability matter more there than payload efficiency.
Choosing one protocol for everything is the mistake. The question is which properties matter on each specific path.



