Lesson 3 · Foundations

Services & codegen

From service { rpc … } to a Go interface you implement.

Your win: declare a service, and explain exactly what make gen-proto-go produces — and the surprising truth that this repo generates with protoc, not buf.

A service is a set of RPC methods

Add a service block to your .proto; each rpc line is one method with a request and a response message.1

proto/spike/v1/email.proto
service EmailModifierService {
    rpc SendEmail(SendEmailRequest) returns (SendEmailResponse);
    rpc BulkSendEmails(BulkSendEmailsRequest) returns (BulkSendEmailsResponse);
}

What codegen produces

A code generator turns that into Go. For each service you get three things:

XServiceServer

An interface with one Go method per rpc. You implement this — it's your handler type.

RegisterXServiceServer

A function to attach your implementation to a running grpc.Server.

XServiceClient

The stub — callers use it to invoke the methods remotely (Lesson 1's f.client).

⚠️ In this repo: protoc, not buf generate make gen-proto-go runs protoc (via proto/gen_go.sh in Docker) with the protoc-gen-go (messages), protoc-gen-go-grpc (service + client), and gateway plugins, writing to pkg/manabuf/. buf is used only for linting & breaking-change checks (make lint-proto). The repo rule is explicit: don't run local buf generate.

Registration wires your implementation in (`SetupGRPC` calls the generated registrar):

cmd/server/spike/init_grpc.go:10
spb.RegisterEmailModifierServiceServer(server, emailModifierSvc)  // your impl
// callers elsewhere: spb.NewEmailModifierServiceClient(conn)
Anchor — the Unimplemented twist Our codegen passes require_unimplemented_servers=false (proto/gen_go.sh), so your server type does not need to embed UnimplementedXServer — though some transport structs still do it for forward-compatibility (recall Go course, Lesson 8 on embedding). Without that flag, forgetting the embed is a compile error.
Do this next

gRPC — Go Quick start (protoc plugins) + Buf docs

The quickstart shows installing protoc-gen-go/-go-grpc and generating stubs — the same plugins our gen_go.sh runs. Buf docs cover the lint/breaking rules we do use.

grpc.io/docs/languages/go/quickstart
buf.build/docs (lint & breaking)

Check yourself (from memory)

Q1. In this repo, Go stubs are generated by…

protoc through proto/gen_go.sh. A common wrong assumption is that buf generates — here it only lints.

Q2. In this repo, buf is used for…

make lint-proto — style + breaking-change detection (guarding the field-number rule from Lesson 2). Not codegen.

Q3. Codegen produces, for each service, a…

An XServiceServer interface (you implement), a Register… func, and an XServiceClient stub.
What does make gen-proto-go produce from a service, and what do you write?
recall, then click to reveal
It runs protoc (not buf) via proto/gen_go.sh to generate, per service: the message structs, an XServiceServer interface, a RegisterXServiceServer function, and an XServiceClient stub — under pkg/manabuf. You write the Go type that implements the server interface (the handler methods). buf only lints / breaking-checks the proto.
Want to see the exact protoc flags in gen_go.sh, or how the gateway plugin fits in? Ask me.

1. gRPC — service definition; Go quickstart.