Files
go-patterns/patterns/documentation.md
T
Rodin 0f1d7e4c06 feat: initial Go patterns guide from stdlib + Kubernetes source study
9 pattern files covering stdlib (structs, interfaces, API conventions, docs, style),
Kubernetes (controller/reconciler, informer/cache, leader election, code generation),
comparison (stdlib vs K8s approaches), and anti-patterns.

All patterns cite exact source files and line numbers.
2026-04-30 06:34:02 +00:00

11 KiB
Raw Blame History

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 630, os/file.go lines 543, log/slog/doc.go lines 630

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 3743, 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 6482 (Handler), bufio/scan.go lines 1427 (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.

Pattern name: [TypeName], [Package.Symbol], [Method] Links

Source citation: net/http/server.go lines 6570, 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 1328, net/http/example_handle_test.go lines 1631

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.

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 1735, time/time.go lines 928933

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 9395

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.

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 7980, os/types.go line 17, regexp/regexp.go lines 7779

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].