Added 'When to Use' subsections with concrete decision triggers and before/after Go code examples to patterns across all directories: - patterns/error-handling.md (3 patterns: sentinels, wrapping, Join) - patterns/concurrency.md (4 patterns: Mutex, Once, done channels, pipelines) - patterns/interfaces.md (4 patterns: small interfaces, accept/return, adapter, optional) - patterns/structs.md (3 patterns: zero-value, constructors, config structs) - patterns/package-design.md (3 patterns: internal/, init(), context keys) - patterns/style.md (3 patterns: interface checks, iota constants, named types) - patterns/testing-advanced.md (3 patterns: table tests, golden files, httptest) - patterns/api-conventions.md (3 patterns: Must, layered API, graceful shutdown) - patterns/documentation.md (2 patterns: examples, deprecated) - kubernetes/patterns.md (3 patterns: controller, workqueue, leader election) - kubernetes/production-go.md (2 patterns: codegen, HandleCrash) - smells/anti-patterns.md (2 anti-patterns: cache mutation, edge-triggered)
12 KiB
Documentation Patterns in the Go Standard Library
1. Package Documentation (doc.go or Package Comment)
Pattern name: Package Doc Comment
Source citation: net/http/doc.go lines 6–30, os/file.go lines 5–43, log/slog/doc.go lines 6–30
What it does: The first file in a package (by convention doc.go, or the main
source file) starts with a // Package xxx ... comment that explains the package's
purpose, key types, and typical usage patterns.
Why: This is the first thing users see in go doc <pkg> and on pkg.go.dev. It
sets context, teaches the mental model, and provides copy-paste examples.
Anti-pattern: No package comment; package comment that just restates the package name ("Package http provides http"); putting documentation in README instead of code.
Code examples from source:
// net/http/doc.go:6-12
/*
Package http provides HTTP client and server implementations.
[Get], [Head], [Post], and [PostForm] make HTTP (or HTTPS) requests:
resp, err := http.Get("http://example.com/")
...
*/
// os/file.go:5-43
// Package os provides a platform-independent interface to operating system
// functionality. The design is Unix-like, although the error handling is
// Go-like; failing calls return values of type error rather than error numbers.
// Often, more information is available within the error. For example,
// if a call that takes a file name fails, such as [Open] or [Stat], the error
// will include the failing file name when printed and will be of type
// [*PathError], which may be unpacked for more information.
// log/slog/doc.go:6-10
/*
Package slog provides structured logging,
in which log records include a message,
a severity level, and various other attributes
expressed as key-value pairs.
*/
2. Section Headers in Package Docs
Pattern name: # Heading in Doc Comments
Source citation: os/file.go lines 37–43, net/http/doc.go (multiple sections)
What it does: Uses # Section Name within the package doc comment to organize
long documentation into navigable sections.
Why: Large packages need structure. Section headers render as links in pkg.go.dev and provide a scannable table of contents.
Anti-pattern: Wall-of-text package docs; using === or --- (not recognized);
too many sections (fragmenting simple docs).
Code example from source:
// os/file.go:37
// # Concurrency
//
// The methods of [File] correspond to file system operations. All are
// safe for concurrent use.
3. Type/Function Comment Convention
Pattern name: // TypeName verb... or // FuncName verb...
Source citation: net/http/server.go lines 64–82 (Handler), bufio/scan.go lines 14–27 (Scanner)
What it does: Every exported identifier's doc comment starts with the identifier name, followed by a verb phrase describing what it does or represents.
Why: go doc extracts the first sentence as a summary. Starting with the name
ensures it reads correctly in both isolation (summary lists) and full context.
This is enforced by convention and checked by linters.
Anti-pattern: Starting with "This function..." or "The Foo type..."; starting with articles ("A Handler is...") for functions (acceptable for types); omitting the comment entirely.
Code examples from source:
// net/http/server.go:64
// A Handler responds to an HTTP request.
// bufio/scan.go:14-17
// Scanner provides a convenient interface for reading data such as
// a file of newline-delimited lines of text.
// net/http/request.go:867
// NewRequest wraps NewRequestWithContext using context.Background.
// os/file.go:389-390
// Open opens the named file for reading.
// regexp/regexp.go:310-312
// MustCompile is like [Compile] but panics if the expression cannot be parsed.
// It simplifies safe initialization of global variables holding compiled regular
// expressions.
4. Doc Links (Square Bracket References)
Pattern name: [TypeName], [Package.Symbol], [Method] Links
Source citation: net/http/server.go lines 65–70, os/file.go line 9
What it does: Doc comments use [SymbolName] to create hyperlinks to other
identifiers. These render as clickable links on pkg.go.dev.
Why: Cross-references help users navigate the API. Links are concise and don't clutter the plain-text rendering.
Anti-pattern: Using full URLs to godoc pages; not linking related types; over-linking (every mention of every type).
Code examples from source:
// net/http/server.go:65-70
// [Handler.ServeHTTP] should write reply headers and data to the [ResponseWriter]
// and then return. Returning signals that the request is finished; it
// is not valid to use the [ResponseWriter] or read from the
// [Request.Body] after or concurrently with the completion of the
// ServeHTTP call.
// os/file.go:9-11
// if a call that takes a file name fails, such as [Open] or [Stat], the error
// will include the failing file name when printed and will be of type
// [*PathError], which may be unpacked for more information.
5. Example Test Functions
Pattern name: func ExampleXxx() / func ExampleType_Method()
Source citation: regexp/example_test.go lines 13–28, net/http/example_handle_test.go lines 16–31
What it does: Functions named Example, ExampleXxx, or ExampleType_Method
in _test.go files serve as both executable tests and documentation. They include
an // Output: comment that go test verifies.
Why: Examples that compile, run, and are verified can never go stale. They appear
in go doc and pkg.go.dev alongside the relevant symbol. They teach by showing
real, working code.
When to Use
Triggers:
- You have a public function/type whose usage isn't obvious from the signature alone
- Your README examples have drifted from the actual API (broken examples in docs)
- You want examples that appear on pkg.go.dev AND are verified by
go test
Example — before:
// README.md (may be stale):
// ```go
// result := mylib.Process("input")
// fmt.Println(result.Data)
// ```
// ← compiles? who knows. API changed last month.
Example — after:
// example_test.go
func ExampleProcess() {
result := mylib.Process("input")
fmt.Println(result.Data)
// Output:
// processed: input
}
// ← go test verifies this compiles and produces the expected output
Anti-pattern: Examples that don't compile; examples without Output comments (not verified); examples in README that drift from reality.
Code examples from source:
// regexp/example_test.go:13-28
func Example() {
// Compile the expression once, usually at init time.
// Use raw strings to avoid having to quote the backslashes.
var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)
fmt.Println(validID.MatchString("adam[23]"))
fmt.Println(validID.MatchString("eve[7]"))
fmt.Println(validID.MatchString("Job[48]"))
fmt.Println(validID.MatchString("snakey"))
// Output:
// true
// true
// false
// false
}
// net/http/example_handle_test.go:16-31
type countHandler struct {
mu sync.Mutex // guards n
n int
}
func (h *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.mu.Lock()
defer h.mu.Unlock()
h.n++
fmt.Fprintf(w, "count is %d\n", h.n)
}
func ExampleHandle() {
http.Handle("/count", new(countHandler))
log.Fatal(http.ListenAndServe(":8080", nil))
}
6. Inline Code Examples in Doc Comments
Pattern name: Indented Code Blocks in Comments
Source citation: os/file.go lines 17–35, time/time.go lines 928–933
What it does: Doc comments include indented code snippets (4 spaces) that render as preformatted code blocks in godoc.
Why: Shows typical usage patterns directly in the doc comment without requiring a separate Example test function. Good for short, illustrative snippets.
Anti-pattern: Non-indented code that doesn't render as code; examples too long for inline (use Example functions instead); examples that reference unexported symbols.
Code examples from source:
// os/file.go:17-21
// Here is a simple example, opening a file and reading some of it.
//
// file, err := os.Open("file.go") // For read access.
// if err != nil {
// log.Fatal(err)
// }
// time/time.go:928-933
// To count the number of units in a [Duration], divide:
//
// second := time.Second
// fmt.Print(int64(second/time.Millisecond)) // prints 1000
//
// To convert an integer number of units to a Duration, multiply:
//
// seconds := 10
// fmt.Print(time.Duration(seconds)*time.Second) // prints 10s
7. Deprecated Annotations
Pattern name: // Deprecated: ... in Doc Comments
Source citation: net/http/server.go line 57 (ErrWriteAfterFlush), os/file.go lines 93–95
What it does: A paragraph starting with Deprecated: marks an identifier as
deprecated and explains what to use instead.
Why: Recognized by tooling (go vet, staticcheck, IDEs). Provides a migration path without breaking backward compatibility.
When to Use
Triggers:
- You have a better replacement for an existing function but can't remove the old one (semver)
- Users are still calling a function that has known issues or a superior alternative
- You want IDEs to show a strikethrough and linters to warn on usage
Example — before:
// Just delete it? Breaks everyone's code.
// Leave it silently? Users never learn about the better way.
Example — after:
// ParseDuration parses a duration string.
//
// Deprecated: Use [time.ParseDuration] instead, which handles
// all standard duration formats.
func ParseDuration(s string) (time.Duration, error) {
return time.ParseDuration(s) // delegate to the replacement
}
Anti-pattern: Removing deprecated APIs (breaks semver); deprecating without suggesting an alternative; using non-standard deprecation markers.
Code example from source:
// net/http/server.go:55-57
// Deprecated: ErrWriteAfterFlush is no longer returned by
// anything in the net/http package. Callers should not
// compare errors against this variable.
ErrWriteAfterFlush = errors.New("unused")
8. Error Documentation Convention
Pattern name: "If there is an error, it will be of type [*XxxError]"
Source citation: os/file.go lines 388, 406
What it does: Functions document the concrete error type they return, enabling callers to type-assert for additional context.
Why: Go's error handling relies on type assertions and errors.Is/As. Knowing
the concrete type lets callers extract structured information (path, operation,
underlying cause).
Anti-pattern: Returning opaque errors with no documented structure; returning different error types from the same function without documenting which.
Code example from source:
// os/file.go:388-390
// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode [O_RDONLY].
// If there is an error, it will be of type [*PathError].
func Open(name string) (*File, error) {
9. Concurrency Documentation
Pattern name: "Safe for concurrent use" / Concurrency Guarantees
Source citation: net/http/transport.go lines 79–80, os/types.go line 17, regexp/regexp.go lines 77–79
What it does: Doc comments explicitly state the concurrency safety of a type or note exceptions where concurrent use is not safe.
Why: Go programs are inherently concurrent. Without explicit documentation, users must guess whether a type needs external synchronization.
Anti-pattern: Leaving concurrency safety undocumented; documenting it inconsistently across methods; saying "thread-safe" (Java-ism, use "safe for concurrent use by multiple goroutines").
Code examples from source:
// net/http/transport.go:79-80
// Transports should be reused instead of created as needed.
// Transports are safe for concurrent use by multiple goroutines.
// os/types.go:17
// The methods of File are safe for concurrent use.
// regexp/regexp.go:77-79
// A Regexp is safe for concurrent use by multiple goroutines,
// except for configuration methods, such as [Regexp.Longest].