This episode dissects gRPC's architecture from the inside: how protobuf becomes client and server code, the role of HTTP/2 multiplexing and HPACK, the stub and channel components, and the metadata, deadlines, and status codes that govern every RPC.

In episode 1 you learned why gRPC exists. Now it's time to open the hood and see how gRPC works. Episode 2 covers the core architecture: how a single .proto file becomes client and server code, how HTTP/2 is used for multiplexing and header compression, and which runtime components are involved in every call.
This understanding isn't just theory. When you later face errors like DeadlineExceeded or unusual latency, you'll know which part is at fault — whether it's the channel, the stub, the network, or the server. Let's start with the journey of a contract.
The gRPC workflow revolves around the protoc compiler. You write a contract in .proto, then protoc generates three things: message types, the server interface, and the client stub. Because every language reads the same contract, there's no more mismatch between teams.
syntax = "proto3";
package greet.v1;
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
service GreetService {
rpc SayHello(HelloRequest) returns (HelloResponse);
rpc Chat(HelloRequest) returns (stream HelloResponse);
}Notice the package greet.v1: this namespace determines the package name in the generated code and becomes part of the full method name, for example greet.v1.GreetService/SayHello.
After code generation, each side gets two distinct artifacts:
client.SayHello(ctx, req) and the stub handles encoding, transport, and decoding.This is the contract-driven development model: the interface is forced to be correct before runtime even starts.
gRPC uses HTTP/2, not HTTP/1.1. Its biggest difference is multiplexing: a single TCP connection can carry many streams at once, and each stream can carry many messages. If REST needs one connection per request, gRPC can handle thousands of concurrent calls on one connection.
The consequence is important: don't open a new channel for every request. A single gRPC channel should be reused, because it's a collection of existing HTTP/2 connections.
HTTP/2 also introduces HPACK, a header compression scheme. Headers like content-type and grpc-status are stored in a table; repeated sends only transmit an index, not the full string. This is one reason gRPC calls are far lighter than REST/JSON, which repeats headers on every request.
Because connections are kept alive, the cost of the TCP and TLS handshake is paid once, not per request. For workloads with many short calls, this savings is significant. Episode 10 will cover how to manage this connection lifecycle explicitly.
A channel is the abstraction of a connection to an address, e.g. localhost:50051. The channel holds configuration like TLS options, keepalive, and load balancing. In Go, a channel is created with grpc.NewClient or grpc.DialContext:
import "google.golang.org/grpc"
conn, err := grpc.NewClient(
"localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatal(err)
}
defer conn.Close()grpc.WithTransportCredentials(insecure.NewCredentials()) marks the connection as plaintext for development; in episode 11 you'll replace it with TLS credentials.
The data that flows is a protobuf message. The gRPC runtime uses a codec for serialization and deserialization: on the client side, messages are encoded to binary before being sent; on the server side, binary is decoded back into language objects. Protobuf also provides field numbering that enables forward compatibility — the topic of episode 8.
Every RPC carries metadata: additional key-value pairs like authentication tokens, trace IDs, or region. Metadata is sent as HTTP/2 headers and can be read on both sides. Episode 6 will use it for auth and logging.
Every gRPC call should have a deadline — a maximum time limit. If exceeded, the client receives a DEADLINE_EXCEEDED status and the RPC is cancelled. Deadlines prevent requests from hanging indefinitely and form the basis of the timeout patterns in episode 15.
gRPC uses standard status codes like OK, NOT_FOUND, INVALID_ARGUMENT, UNAUTHENTICATED, and UNAVAILABLE. Status codes travel with the error message and become a common language between client and server. Episode 5 covers mapping domain errors to the right status codes.
Key takeaways:
.proto file becomes the one contract that generates the client stub and server interface.In episode 3 next, you'll write your first gRPC API with Protocol Buffers — the complete structure of a .proto file, modern v3 data types like enum, oneof, map, and repeated, and how to declare all four RPC patterns: unary, server streaming, client streaming, and bidirectional streaming. Get your editor ready, because from here on we're writing real code.