skip to content

A team is choosing between REST with JSON over HTTP/1.1 and gRPC over HTTP/2 for synchronous calls between two internal services. What concrete technical trade-offs should drive that decision?

level: middleimportance: should knowfreq 65%

answer

  1. protobuf binary vs JSON text
  2. HTTP/2 multiplexing + streaming
  3. codegen contract vs documented contract
  4. browser needs grpc-web proxy

basics

~20 s

REST with JSON is simple, human-readable, and works everywhere, including browsers, but is slower to parse and less strict about structure. gRPC uses a compact binary format and a strict contract, making it faster and safer between services, but harder to read by hand and not natively usable from a browser.

solid answer

~50 s

REST over HTTP/1.1 with JSON is text-based, human-readable, and universally supported by browsers, tools like curl, and every language, which makes it easy to debug and easy for external or public-facing APIs to consume. Its costs are JSON's parsing/serialization overhead, weaker type safety since the schema lives in documentation rather than being enforced, and HTTP/1.1's per-request overhead absent an explicit multiplexing layer. gRPC uses Protocol Buffers, a compact binary format with a strongly-typed .proto contract, over HTTP/2, which gives smaller payloads, faster serialization, generated client/server code in multiple languages, and native support for streaming (client, server, or bidirectional) over a single multiplexed connection. Its costs are that payloads aren't human-readable without tooling, browser support requires a proxy layer like grpc-web, and the tighter contract means both sides need coordinated codegen and versioning discipline. The practical rule of thumb: gRPC for internal, high-throughput, latency-sensitive service-to-service calls where you control both ends; REST/JSON for public APIs, browser clients, or anywhere debuggability and broad compatibility matter more than raw performance.

go deeper

for a junior

Should know that REST/JSON is text and human-readable while gRPC is binary and requires generated code, and that browsers can't call gRPC directly.

for a middle

Should articulate the concrete performance mechanisms (binary serialization, HTTP/2 multiplexing/streaming) and the concrete cost (codegen coordination, no native browser support) rather than just 'gRPC is faster.'

for a senior

Should discuss version-skew and backward-compatibility discipline for .proto contracts, and reason about when the switch is and isn't worth it given existing infrastructure and traffic volume.

for a principal

Should reason about this as an org-wide standardization decision: tooling investment, mixed-fleet migration strategy, and how the choice interacts with API-gateway and edge-vs-internal boundary design across many teams, not just a single service pair.

## Two mechanisms, three axes of difference REST over HTTP with JSON and gRPC over HTTP/2 are both synchronous request-response mechanisms, but they differ in wire format, contract strictness, and transport capabilities in ways that matter a great deal once a system has dozens of services calling each other constantly. - **REST** typically means resources addressed by URL, standard HTTP verbs (`GET`, `POST`, `PUT`, `DELETE`), and a JSON body for the payload. JSON is plain text: human-readable, trivially inspectable with `curl` or a browser dev tools panel, and parseable by literally every programming language's standard library. - **gRPC** instead defines a service contract in a `.proto` file, specifying exact method names, request/response message shapes, and field types, then uses a code generator to produce client stubs and server skeletons in whatever languages you need. Those messages are serialized with Protocol Buffers, a compact binary encoding, and sent over HTTP/2 rather than HTTP/1.1. ## Why gRPC exists The reason gRPC exists is that JSON-over-HTTP/1.1 has real costs at scale that only show up once you're making a large volume of internal calls. - **Serialization cost.** JSON serialization and parsing is comparatively slow and CPU-heavy because it's text that has to be tokenized and type-inferred; protobuf's binary format skips most of that work and produces smaller payloads, which matters for both latency and network cost at high request volume. - **Connection cost.** HTTP/1.1 also opens one request per connection at a time (absent pipelining hacks most stacks don't use), so a client calling many endpoints either serializes requests or opens many connections; HTTP/2 multiplexes many concurrent requests and responses over a single connection, cutting connection overhead and enabling true streaming, including a client streaming many messages, a server streaming many responses, or both directions at once, none of which REST/JSON has a standard mechanism for. ## The other direction The trade-offs run in the other direction too. **JSON's human-readability is not a minor convenience:** being able to `curl` an endpoint and read the response directly, or inspect a request in a browser's network tab, is what makes REST APIs approachable for debugging, for third-party integrators, and for browser-based frontends, none of which can speak gRPC natively since browsers don't expose raw HTTP/2 trailers the way gRPC needs; you'd need a translation proxy like grpc-web or a REST gateway in front of the gRPC service. **REST's looser contract**, where the shape of the JSON is documented (often via OpenAPI) rather than compiler-enforced, is also a double-edged sword: it's easier to evolve informally (add an optional field, nobody's build breaks), but that same looseness means a typo'd field name or a subtly wrong type only fails at runtime, sometimes deep in production, rather than at compile time. gRPC's generated stubs catch a whole class of contract mismatches before the code ever runs, at the cost of needing coordinated codegen: both the calling service and the called service need to regenerate and redeploy code when the `.proto` contract changes, which is more process overhead than just updating a JSON example in a wiki page. ## Where the breakages cluster In production: | Choice | Where the trouble concentrates | |---|---| | **REST/JSON** | Failure modes tend to be about malformed or unexpected payloads slipping through at runtime, since there's no compiler to catch a client sending a string where the server code expects a number, and about the aggregate cost of serialization overhead showing up as CPU time in flame graphs once request volume gets high enough. | | **gRPC** | Failure modes tend to cluster around version skew, where an older client generated from an outdated `.proto` talks to a newer server (or vice versa), and around HTTP/2-specific infrastructure issues, since some older load balancers, proxies, and corporate firewalls don't handle HTTP/2 or gRPC's use of trailers correctly, which can manifest as mysterious connection resets that don't happen with plain HTTP/1.1. | ## Where the split usually lands A concrete, well-known real-world split: Google, which created both Protocol Buffers and gRPC, uses gRPC extensively for internal service-to-service calls across its own infrastructure where both ends are Google-controlled services and performance at massive scale matters, while public-facing Google APIs are generally still exposed as REST/JSON (or a REST-like interface) precisely because external developers need something they can read, debug, and call from a browser or a simple HTTP client without needing gRPC tooling installed. That same split, gRPC internally, REST/JSON at the edge or for public APIs, is the common pattern most organizations converge on rather than picking one universally.

  • Why can't a browser call a gRPC service directly the way it calls a REST endpoint?
    gRPC relies on HTTP/2 features, specifically trailers (headers sent after the response body) to carry status information, that browser fetch/XHR APIs don't expose to JavaScript. The standard workaround is grpc-web, a variant protocol plus a translating proxy that sits in front of the gRPC service and converts between what the browser can send and true gRPC framing.
  • What operational problem does version skew cause in a gRPC-based system, and how do teams usually guard against it?
    If a service updates its .proto contract by removing or renumbering a field and a caller hasn't regenerated its client stub, calls can fail or silently misbehave depending on how the change was made. Teams guard against this by following protobuf's backward-compatibility rules strictly, never reusing or renumbering field numbers, only adding new optional fields, and by treating .proto files as a versioned, reviewed contract similar to a public API schema.
  • In what situation would you still choose REST/JSON for an internal, high-throughput service-to-service call despite gRPC's performance edge?
    When the team's operational tooling, debugging habits, or existing infrastructure (load balancers, API gateways, logging pipelines) is built around HTTP/1.1 and JSON and switching to gRPC would require nontrivial investment across many services for a performance gain the current traffic volume doesn't actually need. Premature adoption of gRPC purely for its reputation, without a concrete latency or throughput problem to solve, often isn't worth the added tooling and debugging friction.

REST/JSON is like writing a letter in plain English that anyone can open and read; gRPC is like sending a message in a compact, pre-agreed shorthand that only people who've learned the shorthand (via generated code) can read quickly, but that shorthand is faster to write, faster to read, and catches most spelling mistakes before you even send it.

saying these in an interview costs you the question

  • Claims gRPC is 'always faster' without naming why (binary format + HTTP/2 multiplexing)
  • Doesn't know gRPC needs a proxy layer for browser clients
  • Thinks REST can't use HTTP/2 at all
  • Can't explain what a .proto file is or does
  • Assumes JSON's looser contract has no runtime cost or risk

context