Reddit Backend in Microservices
Four Node processes behind an HTTP gateway, talking over gRPC with hand-written Protocol Buffer contracts. A one-week build from 2022 to learn protobufs properly — and honest about the distance between an exercise and a system.
Overview
Reddit's core nouns — users, subreddits, posts, comments — split across four Node processes: an Express gateway that speaks HTTP to the browser, and three gRPC services behind it. 793 lines, fifteen commits, seven days in October 2022. I wrote all of it, working through a talk-through to learn Protocol Buffers properly, and I have not touched it since.
The contract is the point
Three .proto files define every call between the gateway and the services, and both sides compile against them. That is the whole reason to reach for gRPC instead of an internal REST call: a field you rename breaks the build, rather than breaking a response body someone notices next Tuesday. Writing those schemas by hand — deciding what a Post is on the wire as opposed to in the database — is the part that stayed with me.
The decision I would still defend
The gateway never verifies a token and never holds the signing secret. It calls isAuthenticated on the user service, which checks the signature and re-reads the user from the database, so a still-valid token for a deleted account is refused. The verified identity is then attached to the request, and every write downstream uses that — never a user id the client sent. The post service independently re-checks ownership before an update. The client never gets to say who it is. The cost is a round trip and a database read on every authenticated request, with no cache and no deadline on the call, which is exactly where a reviewer should push.
What it is not
It is not a microservices system, and calling it one would be the easy lie. The three services share a single MongoDB, and the post model holds references into the other two services' collections — that is a distributed monolith with four processes. Every address is a hardcoded localhost literal. There is no container, no service discovery, no broker, no retries, no health checks, and no tests. The API has no listing endpoint at all, so nothing can be browsed; you have to already know an ObjectId. It is JavaScript, not TypeScript.
Why it is still here
Because the calibrated version is more useful than the flattering one. It shows I can define a wire contract, put an authentication boundary in the right place, and name the gap between a week of learning and a running system without being asked. The production version of that instinct lives in work I cannot publish.