From bec92ec0dfdf4898a1229838a9570c06d9be0b40 Mon Sep 17 00:00:00 2001 From: Haven Xia Date: Thu, 9 Jul 2026 13:39:47 -0700 Subject: [PATCH] Add Go code style guide. --- docs/code-style-guide.md | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 docs/code-style-guide.md diff --git a/docs/code-style-guide.md b/docs/code-style-guide.md new file mode 100644 index 000000000..8558b2d6d --- /dev/null +++ b/docs/code-style-guide.md @@ -0,0 +1,46 @@ +# Code Style Guide + +Go conventions for this repository — written for humans and coding agents +alike. The [API style guide](api-style-guide.md) governs the proto surface; +this document governs the Go code behind it. Repository layout is covered by +[code layout](dev/code-layout.md). + +The baseline is [Effective Go](https://go.dev/doc/effective_go), the +[Google Go style guide](https://google.github.io/styleguide/go/), and `gofmt`. +This document records only decisions that are specific to this project. + +## Proto field access: check presence, don't default it + +Generated getters (e.g. `req.GetActor().GetName()`) return zero values when any +message in the chain is nil. That is convenient and dangerous: a required +message the caller forgot to set silently becomes `""`, and the bug surfaces +far from its cause. + +- A getter chain must never stand in for a presence check. Whenever a field + is required at this point in the code, check it explicitly and fail loudly: + `INVALID_ARGUMENT` at the API boundary, a real error (not a guessed zero + value) elsewhere. +- Once the boundary has validated presence, getters downstream are fine, as is + the guarded form for optional messages: + + ```go + if wass := worker.Assignment; wass != nil { + // Fields of wass were validated on write; use them directly. + } + ``` + +- Absence of a group of fields that are set and cleared together is + represented by a nil message (`worker.Assignment == nil`), never by probing + a scalar field inside it for its zero value. + +## Testing + +- Standard library `testing` only — no assertion or mocking frameworks. +- Table-driven tests with `t.Run` subtests are the default shape. +- Prefer a real fake when one exists: `miniredis` for the store, `envtest` for + the Kubernetes API. Release resources with `t.Cleanup`. + +## TODOs + +Deferred decisions are recorded in code as `TODO(username): ...`, placed where +the decision will eventually have to be made.