Testing unary gRPC services in Go

We don’t want to test gRPC or an HTTP server itself, we simply want to test our method’s logic. The simple answer to this question is to de-couple gRPC’s work from the actual work. – John Doak, Testing gRPC methods That advice is right most of the time. If your handler is a thin shell over business logic that lives behind an interface, you can test the logic without gRPC at all. Inject a fake, call the method, check the result. ...

March 23, 2026

Is passing user ID through context an antipattern?

Paweł Grzybek reached out after reading What belongs in Go’s context values? with a question about their auth middleware and the handler that reads the user ID from context: I validate the session in middleware, and the session record in the DB holds the user ID, which I put in the context for handlers to use later. According to your post, this is an antipattern because the handler can’t work without that value. But if I don’t use context here, I’d have to hit the sessions table again in the handler. Is this actually wrong, and if so, how do I avoid the double DB lookup? ...

March 18, 2026

What belongs in Go's context values?

Another common question popped up in r/golang: I’ve been reading mixed opinions lately about using context to pass values like request IDs, auth info, or tenant IDs through middleware layers. Some people argue it’s fine and exactly what context was extended for after 1.7. Others say it’s a code smell that leads to hidden dependencies and untestable code. I see both sides. On one hand it keeps function signatures clean. On the other hand you lose compile-time safety and it’s not obvious what a function needs from ctx. ...

March 17, 2026

Wrapping a gRPC client in Go

Yesterday I wrote a shard on exploring the etcd codebase. One of the things that stood out was how the clientv3 package abstracts out the underlying gRPC machinery. etcd is a distributed key-value store where the server and client communicate over gRPC. But if you’ve only ever used clientv3 and never peeked into the internals, you wouldn’t know that. You call resp, err := client.Put(ctx, "key", "value") and get back a *PutResponse. It feels like a regular Go library. The fact that gRPC and protobuf are involved is an implementation detail that the client wrapper keeps away from you. ...

March 15, 2026

In praise of the etcd codebase

I’ve been writing a lot of Go gRPC services these days at work - database proxies, metadata services, storage orchestration control plane, etc. They require me to go a bit deeper into protobuf and Go gRPC tooling than you’d typically need to. So I started poking around OSS gRPC codebases to pick up conventions. I was mainly looking for pointers on how to organize protobuf definitions, wire up server-side metrics and interceptors, and build ergonomic client wrappers. The default answer here is often “go read the Docker or Kubernetes codebase.” But both of those are pretty huge and take forever to get accustomed to. ...

March 14, 2026

Tap compare testing for service migration

Throughout the years, I’ve been part of a few medium- to large-scale system migrations. As in, rewriting old logic in a new language or stack. The goal is usually better scalability, resilience, and maintainability, or more flexibility to adapt to changing requirements. Now, whether rewriting your system is the right move is its own debate. A common question that shows up during a migration is, “How do we make sure the new system behaves exactly like the old one, minus the icky parts?” Another one is, “How do we build the new system while the old one keeps changing without disrupting the business?” ...

December 13, 2025

Gateway pattern for external service calls

No matter which language you’re writing your service in, it’s generally a good idea to separate your external dependencies from your business-domain logic. Let’s say your order service needs to make an RPC call to an external payment service like Stripe when a customer places an order. Usually in Go, people make a package called external or http and stash the logic of communicating with external services there. Then the business logic depends on the external package to invoke the RPC call. This is already better than directly making RPC calls inside your service functions, as that would make these two separate concerns (business logic and external-service wrangling) tightly coupled. Testing these concerns in isolation, therefore, would be a lot harder. ...

August 3, 2025

Notes on building event-driven systems

I spent the evening watching this incredibly grokkable talk on event-driven services by James Eastham at NDC London 2024. Below is a cleaned-up version of my notes. I highly recommend watching the full talk if you’re interested before reading this distillation. The curse of tightly coupled microservices Microservices often start with HTTP-based request-response communication, which seems straightforward but quickly becomes a pain as systems grow. Coupling - where one service depends on another - creates a few issues. Take the order processing service in a fictional Plant-Based Pizza company. It has to talk to the pickup service, delivery service, kitchen, and loyalty point service. They’re all tied together, so if one fails, the whole system could go down. ...

September 21, 2024

Protobuffed contracts

People typically associate Google’s Protocol Buffer with gRPC services, and rightfully so. But things often get confusing when discussing protobufs because the term can mean different things: A binary protocol for efficiently serializing structured data. A language used to specify how this data should be structured. In gRPC services, you usually use both: the protobuf language in proto files defines the service interface, and then the clients use the same proto files to communicate with the services. ...

May 10, 2024