fix: add 'When to use' to every pattern (was missing)

This commit is contained in:
Rodin
2026-04-30 13:29:50 -07:00
parent 65cae45f13
commit dfe03e0675
+36 -1
View File
@@ -34,6 +34,9 @@ with a `Read` method satisfies `Reader` — no explicit declaration
needed. The Go stdlib has 15 compositions of just 4 primitives in needed. The Go stdlib has 15 compositions of just 4 primitives in
`io/io.go`. `io/io.go`.
**When to use:** Defining extension points, function parameters,
and dependency injection boundaries.
**When NOT to use:** When the interface genuinely requires multiple **When NOT to use:** When the interface genuinely requires multiple
methods that always go together (e.g., `http.Handler` is one method methods that always go together (e.g., `http.Handler` is one method
but `sort.Interface` is three because Len/Less/Swap are inseparable). but `sort.Interface` is three because Len/Less/Swap are inseparable).
@@ -55,6 +58,9 @@ func NewReader(rd io.Reader) *Reader
structs means callers get full access to methods and fields without structs means callers get full access to methods and fields without
type assertions. type assertions.
**When to use:** Public API functions that take collaborators or
return constructed objects.
**When NOT to use:** When you need to return different types based on **When NOT to use:** When you need to return different types based on
input (return an interface). When the concrete type is unexported input (return an interface). When the concrete type is unexported
(return an interface to hide it). (return an interface to hide it).
@@ -80,6 +86,9 @@ var ErrLength = errors.New("encoding/hex: odd length hex string")
specific cases. The error message includes the package name for specific cases. The error message includes the package name for
context. context.
**When to use:** Errors that represent a known, documented condition
that callers may want to handle differently.
**When NOT to use:** For errors that callers never need to distinguish. **When NOT to use:** For errors that callers never need to distinguish.
For errors that carry dynamic context (use error types instead). For errors that carry dynamic context (use error types instead).
@@ -96,6 +105,9 @@ return fmt.Errorf("cannot parse %q as JSON number: %w", val, strconv.ErrSyntax)
**Why:** Wrapping creates a chain. Callers can `errors.Is()` to find **Why:** Wrapping creates a chain. Callers can `errors.Is()` to find
the root cause while seeing the full context path. the root cause while seeing the full context path.
**When to use:** Every time you propagate an error up the call stack
and the caller might need to identify the root cause.
**When NOT to use:** When the original error's identity should be **When NOT to use:** When the original error's identity should be
hidden (use `%v` instead of `%w` to break the chain intentionally). hidden (use `%v` instead of `%w` to break the chain intentionally).
@@ -137,6 +149,9 @@ func TestSetenv(t *testing.T) {
is one struct literal. The pattern is universal in Go's own tests is one struct literal. The pattern is universal in Go's own tests
(1,811 test files use it). (1,811 test files use it).
**When to use:** Any function with 3+ meaningful input variations.
The default testing pattern in Go.
**When NOT to use:** Single-case tests. Tests where setup varies **When NOT to use:** Single-case tests. Tests where setup varies
significantly between cases (use separate test functions). significantly between cases (use separate test functions).
@@ -148,6 +163,9 @@ ignores it.
**Why:** Convention enforced by the toolchain — `testdata/` is never **Why:** Convention enforced by the toolchain — `testdata/` is never
compiled. Golden files, sample inputs, and expected outputs live here. compiled. Golden files, sample inputs, and expected outputs live here.
**When to use:** Golden files, sample inputs, certificates, expected
outputs — any file your tests read but never modify.
**When NOT to use:** Generated test data (create it in TestMain or **When NOT to use:** Generated test data (create it in TestMain or
setup). setup).
@@ -173,6 +191,8 @@ src/
**Why:** The import path IS the directory path. `import "fmt"` loads **Why:** The import path IS the directory path. `import "fmt"` loads
`src/fmt/`. No indirection, no wrapper directories. `src/fmt/`. No indirection, no wrapper directories.
**When to use:** Always. Every Go project. Import path = directory.
**When NOT to use:** Never. There is no legitimate reason for a `pkg/` **When NOT to use:** Never. There is no legitimate reason for a `pkg/`
directory in Go. (The `pkg/` convention from early community projects directory in Go. (The `pkg/` convention from early community projects
was a mistake that the Go team never endorsed.) was a mistake that the Go team never endorsed.)
@@ -191,7 +211,10 @@ import "internal/singleflight"
build error if they try to import your internals. This is stronger than build error if they try to import your internals. This is stronger than
unexported identifiers (which still allow same-package access). unexported identifiers (which still allow same-package access).
**When NOT to use:** Code that's stable enough for public API (promote **When to use:** Utility code shared across packages that is NOT
ready for (or appropriate as) public API.
**When NOT to use:** Code that is stable enough for public API (promote
it). Code only used by one package (keep it unexported within that it). Code only used by one package (keep it unexported within that
package). package).
@@ -215,6 +238,9 @@ func (c *Client) do(ctx context.Context, req *Request) (*Response, error)
values. First-parameter position is a universal convention — every Go values. First-parameter position is a universal convention — every Go
developer knows to look for it there. developer knows to look for it there.
**When to use:** Any function that does I/O, blocks, or might be
cancelled by the caller.
**When NOT to use:** Pure computation (no I/O, no blocking). Package- **When NOT to use:** Pure computation (no I/O, no blocking). Package-
level init functions. Short-lived operations that can't be cancelled. level init functions. Short-lived operations that can't be cancelled.
@@ -227,6 +253,9 @@ spawn goroutines.
(exception: `net/http` server). Libraries that spawn goroutines create (exception: `net/http` server). Libraries that spawn goroutines create
lifecycle management problems — who stops them? who waits for them? lifecycle management problems — who stops them? who waits for them?
**When to use:** Pure libraries that transform data or compute
results. Let callers decide on concurrency.
**When NOT to use:** Servers (http.Server manages connections). **When NOT to use:** Servers (http.Server manages connections).
Background work that the caller explicitly requested (provide a Background work that the caller explicitly requested (provide a
`Start`/`Stop` interface). `Start`/`Stop` interface).
@@ -257,6 +286,9 @@ package fmt
create sections in pkg.go.dev. `doc.go` is conventional — reviewers create sections in pkg.go.dev. `doc.go` is conventional — reviewers
know where to find the overview. know where to find the overview.
**When to use:** Any package with a non-trivial API surface that
needs an overview explaining its purpose and structure.
**When NOT to use:** Small packages where the package comment fits **When NOT to use:** Small packages where the package comment fits
naturally in the main file. naturally in the main file.
@@ -287,6 +319,9 @@ in docs. They can't go stale because the build fails if they break.
**Why:** Package names prefix every exported identifier. `bytes.Buffer` **Why:** Package names prefix every exported identifier. `bytes.Buffer`
reads well. `utilities.Buffer` doesn't. reads well. `utilities.Buffer` doesn't.
**When to use:** Every package you create. Pick the shortest noun
that accurately describes the package's single responsibility.
**When NOT to use:** Never use plurals (`utils`, `helpers`, `models`). **When NOT to use:** Never use plurals (`utils`, `helpers`, `models`).
Never use generic names that could apply to anything. Never use generic names that could apply to anything.