Weekly Review — Month 5 · Week 1 (Days 113–119)¶
📅 The Week in One Line¶
Built the mental model of gRPC from the wire up: protobuf schema → protoc/buf codegen → unary RPC → server/client/bidi streaming → deadlines, metadata, and the status-code error model.
✅ What I Completed¶
- Day 113 — Protocol Buffers &
.protofiles (proto3, field numbers, wire format) - Day 114 — Code generation with
protoc&buf(two plugins, two output files) - Day 115 — Unary RPCs (
grpc.NewServer/grpc.NewClient, context-first methods) - Day 116 — Server & client streaming (
Send/Recv/io.EOF,SendAndClose) - Day 117 — Bidirectional streaming (one goroutine per direction, half-close)
- Day 118 — Deadlines, metadata & status codes (context triad, code→HTTP map)
- Day 119 — Review + active recall
- Mini-project: stdlib models of the wire format, framing, deadlines, and the status model
- Exercises solved: 3 (
varint,frames,statuscode) — all green
💡 Lessons Learned¶
- On the wire there are no field names — only
(field_number << 3) | wire_type. That single fact explains proto3's compatibility rules: rename freely, never renumber/reuse. - Codegen is two separate plugins:
protoc-gen-go(messages,*.pb.go) andprotoc-gen-go-grpc(stubs,*_grpc.pb.go). EmbedUnimplementedXxxServerfor forward compatibility. - A stream is just length-prefixed frames (5-byte header: flag + big-endian length).
io.EOFis the clean terminator, never an error to log as failure. - The one-goroutine-per-direction rule for streams is the same one-writer discipline as channels; violating it corrupts framing.
- Deadline, cancellation, and metadata all ride the
context— derive child contexts, never reset them, or you silently drop the deadline. - Return
status.Error(codes.X, msg), not plain errors, so callers can branch on the code and a gateway can map to HTTP.
💪 Strengths (what clicked)¶
- Decoding varints/zigzag and gRPC frames by hand — the stdlib examples made the format concrete.
- The
Send/Recv/io.EOFprotocol across all three streaming shapes.
🧩 Weaknesses (what's still fuzzy)¶
- Surfacing the send goroutine's error cleanly in a bidi client (errgroup vs. channel).
- Deadline budgeting across multiple hops.
- Rich status details (
status.WithDetails) — only touched the surface.
🔁 Spaced-Repetition Re-quiz (topics from earlier weeks)¶
- Q: (M4 — concurrency) What's the zero value of a channel and what happens if you send on it?
A
nil; a send (or receive) on anilchannel blocks forever. Useful to disable aselectcase. - Q: (M2 — slices) Why can
appendmutate a slice the caller still holds?A
If the backing array has spare capacity,appendwrites in place, so an aliased slice sees the change; once it grows, a new array is allocated and aliasing breaks. Don't rely on either — copy if you need isolation. - Q: (M3 — errors) What does
errors.Isdo that==doesn't?A
It unwraps the error chain (viaUnwrap), matching a wrapped sentinel anywhere in the chain — e.g. an error wrapped with%waroundio.EOF. - Q: (M1 — interfaces) Why can a non-nil interface hold a nil pointer and not equal
nil?A
An interface is (type, value). A nil*Tstored in an interface gives it a non-nil type word, soiface != nileven though the underlying pointer is nil — the classic nil-interface trap.
🎯 Action Items¶
- Write a small bidi client using
errgroupto propagate the first error and cancel both directions. - Add
status.WithDetails/errdetailsto one handler and read it on the client. - Sketch a per-hop deadline-budget helper.
🚀 Next Week Goals¶
- Interceptors (unary + stream), middleware chaining, and auth.
- TLS / transport credentials for production.
- gRPC-Gateway / JSON transcoding and
grpc-status → HTTPat the edge.
📊 Metrics¶
| Hours | Days hit | Exercises | Commits | Avg confidence |
|---|---|---|---|---|
| 10.5 | 7/7 | 3 | 7 | 3.⅗ |
Suggested commit: docs(journal): month 5 week 1 review