52503d24c2
Audited all file:line citations against golang/go HEAD. 97 citations checked; 9 had drifted by 1-20 lines (off-by-one or small structural shifts in the stdlib source). Updated both inline code block comments and corresponding GitHub #L anchor links. Changes per file: patterns/api-conventions.md - strings/builder.go WriteString (92→112), String (48→46) patterns/configuration.md - crypto/tls/common.go Time field (572→575) patterns/documentation.md - net/http/server.go Handler comment (64→65), os/file.go example (17→16) patterns/structs.md - os/types.go File struct (16→15), strings/builder.go copyCheck (25→32) patterns/style.md - net/http/server.go TLSConfig (3041→3040), import block (8→9)
648 lines
21 KiB
Markdown
648 lines
21 KiB
Markdown
# Code Style Patterns in the Go Standard Library
|
||
|
||
|
||
**Source:** [golang/go](https://github.com/golang/go) at commit [`17bd5ab`](https://github.com/golang/go/tree/17bd5ab8c650155dd2bd09f7005726552639eea0)
|
||
## 1. Naming Conventions: mixedCaps (No Underscores)
|
||
|
||
**Pattern name:** mixedCaps / MixedCaps
|
||
|
||
**Source citation:** All stdlib code (enforced by `gofmt` convention, documented in Effective Go)
|
||
|
||
**What it does:** All identifiers use mixedCaps (unexported) or MixedCaps (exported).
|
||
Underscores are never used in Go names except for test helpers and generated code.
|
||
|
||
**Why:** Consistent casing makes code scannable. The exported/unexported distinction
|
||
is communicated solely through initial capitalization — no separate `public`/`private`
|
||
keywords needed.
|
||
|
||
**Anti-pattern:** `snake_case` names; `ALL_CAPS` for constants; Hungarian notation
|
||
(`strName`, `iCount`).
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// net/http/server.go — exported
|
||
type Server struct { ... }
|
||
func ListenAndServe(addr string, handler Handler) error
|
||
|
||
// net/http/server.go — unexported
|
||
func (s *Server) shuttingDown() bool
|
||
const shutdownPollIntervalMax = 500 * time.Millisecond
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Acronyms Are All-Caps
|
||
|
||
**Pattern name:** Acronym Capitalization
|
||
|
||
**Source citation:** [net/http/request.go#L130](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/request.go#L130) (`URL`), [net/http/server.go#L3040](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L3040) (`TLSConfig`), [encoding/json/stream.go#L280](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/stream.go#L280) (`JSON`)
|
||
|
||
**What it does:** Acronyms and initialisms (URL, HTTP, ID, JSON, XML, HTML, TLS, TCP)
|
||
are always fully capitalized when exported, and fully lowercased when unexported.
|
||
|
||
**Why:** Consistency. `URL` not `Url`, `ID` not `Id`, `HTTP` not `Http`. This
|
||
applies even mid-word: `ServeHTTP`, `xmlEncoder`, `htmlEscape`.
|
||
|
||
**Anti-pattern:** `Url`, `Http`, `Json`, `Id` — mixing cases within an acronym.
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// net/http/request.go:130
|
||
URL *url.URL
|
||
|
||
// net/http/request.go:822
|
||
func ParseHTTPVersion(vers string) (major, minor int, ok bool)
|
||
|
||
// net/http/server.go:3040
|
||
TLSConfig *tls.Config
|
||
|
||
// encoding/json/stream.go:280
|
||
var _ Marshaler = (*RawMessage)(nil)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. File Organization by Responsibility
|
||
|
||
**Pattern name:** One Concept Per File
|
||
|
||
**Source citation:** `net/http/` directory structure
|
||
|
||
**What it does:** Large packages split code into files by topic/type: `client.go`,
|
||
`server.go`, `transport.go`, `request.go`, `response.go`, `cookie.go`, `header.go`,
|
||
`fs.go`, `doc.go`. Each file is focused.
|
||
|
||
**Why:** Navigability. When you want to find client logic, you open `client.go`.
|
||
Files stay manageable sizes. Related code lives together.
|
||
|
||
**Anti-pattern:** One giant file with everything; splitting by access level
|
||
(`public.go` / `private.go`); splitting by method count rather than concept.
|
||
|
||
**File layout from `net/http/`:**
|
||
|
||
```
|
||
client.go — Client type and methods
|
||
transport.go — Transport type (low-level RoundTripper)
|
||
server.go — Server, Handler, ServeMux
|
||
request.go — Request type and parsing
|
||
response.go — Response type and reading
|
||
cookie.go — Cookie parsing and serialization
|
||
header.go — Header type and canonicalization
|
||
fs.go — FileServer, file serving
|
||
doc.go — Package documentation
|
||
clone.go — Clone helpers
|
||
method.go — HTTP method constants
|
||
pattern.go — URL pattern matching (ServeMux routing)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Blank Identifier for Interface Compliance
|
||
|
||
**Pattern name:** `var _ Interface = (*Type)(nil)`
|
||
|
||
**Source citation:** [io/io.go#L645](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L645), [os/file.go#L747](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L747), [encoding/json/stream.go#L280](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/stream.go#L280)
|
||
|
||
**What it does:** A package-level `var _ InterfaceName = (*ConcreteType)(nil)` declares
|
||
that the concrete type must satisfy the interface. The compiler verifies this at
|
||
build time.
|
||
|
||
**Why:** Catches interface drift at compile time without creating an instance. The
|
||
blank identifier discards the value — this is purely a static assertion.
|
||
|
||
**When to Use**
|
||
|
||
**Triggers:**
|
||
- You've defined a type that MUST satisfy an interface (implements `io.Reader`, `http.Handler`, etc.)
|
||
- You want a compile-time guarantee that catches drift when you add/remove methods
|
||
- You're writing a package with multiple types implementing the same interface
|
||
|
||
**Example — before:**
|
||
```go
|
||
type MyWriter struct{ buf bytes.Buffer }
|
||
|
||
func (w *MyWriter) Write(p []byte) (int, error) { return w.buf.Write(p) }
|
||
|
||
// Months later, someone renames Write to WriteBuf...
|
||
// No compile error — only discovered at runtime when passed as io.Writer
|
||
```
|
||
|
||
**Example — after:**
|
||
```go
|
||
// Compile-time check: if MyWriter stops implementing io.Writer, this fails to build
|
||
var _ io.Writer = (*MyWriter)(nil)
|
||
|
||
type MyWriter struct{ buf bytes.Buffer }
|
||
|
||
func (w *MyWriter) Write(p []byte) (int, error) { return w.buf.Write(p) }
|
||
```
|
||
|
||
**When NOT to Use**
|
||
|
||
**Don't use interface compliance checks when:**
|
||
- The type is unexported AND only used locally (the compiler catches it at the call site anyway)
|
||
- You're asserting against an interface you don't own and it changes frequently (creates churn)
|
||
- The interface is implemented implicitly and you're just cargo-culting the pattern for every type
|
||
|
||
**Over-application example:**
|
||
```go
|
||
// Every single struct gets a compliance check, even trivial unexported ones
|
||
var _ fmt.Stringer = (*internalHelper)(nil) // only used in one function in this file
|
||
var _ fmt.Stringer = (*anotherHelper)(nil) // also never passed as Stringer anywhere
|
||
```
|
||
|
||
**Better alternative:**
|
||
```go
|
||
// Skip the check for types that are only used locally — the compiler
|
||
// will catch the issue at the point of use:
|
||
func printThing(s fmt.Stringer) { fmt.Println(s.String()) }
|
||
printThing(&internalHelper{}) // compiler error here if String() is missing
|
||
|
||
// DO use it for exported types that implement external interfaces:
|
||
var _ io.ReadWriteCloser = (*MyConnection)(nil) // users depend on this contract
|
||
```
|
||
|
||
**Why:** The pattern exists for API contracts — types that *must* satisfy an interface for consumers. For unexported types used only locally, the compiler already catches mismatches at the call site. Adding the check everywhere is noise.
|
||
|
||
**Anti-pattern:** Relying on tests to catch interface conformance; skipping the check
|
||
and discovering the mismatch at runtime; using reflection.
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// io/io.go:645
|
||
var _ ReaderFrom = discard{}
|
||
|
||
// os/file.go:747-750
|
||
var _ fs.StatFS = dirFS("")
|
||
var _ fs.ReadFileFS = dirFS("")
|
||
var _ fs.ReadDirFS = dirFS("")
|
||
var _ fs.ReadLinkFS = dirFS("")
|
||
|
||
// encoding/json/stream.go:280-281
|
||
var _ Marshaler = (*RawMessage)(nil)
|
||
var _ Unmarshaler = (*RawMessage)(nil)
|
||
|
||
// net/http/server.go:4071
|
||
var _ Pusher = (*timeoutWriter)(nil)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Named Return Values
|
||
|
||
**Pattern name:** Named Returns for Documentation (and Defer)
|
||
|
||
**Source citation:** [io/io.go#L87](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L87), 100, 314, 387; [os/file.go#L140](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L140), 175
|
||
|
||
**What it does:** Return values are given names when the names add documentary value
|
||
(clarifying which int is what) or when `defer` needs to modify the return value.
|
||
|
||
**Why:** `(n int, err error)` is immediately understandable — `n` is the byte count.
|
||
Named returns also enable `defer func() { err = wrap(err) }()` patterns.
|
||
|
||
**Anti-pattern:** Naming returns for trivial functions where the types are
|
||
self-explanatory; using named returns as implicit variables throughout the function
|
||
body (confusing naked returns); always using naked `return` statements.
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// io/io.go:87 — Interface documentation
|
||
type Reader interface {
|
||
Read(p []byte) (n int, err error)
|
||
}
|
||
|
||
// io/io.go:100
|
||
type Writer interface {
|
||
Write(p []byte) (n int, err error)
|
||
}
|
||
|
||
// io/io.go:387 — Named return used with defer-style logic
|
||
func Copy(dst Writer, src Reader) (written int64, err error) {
|
||
return copyBuffer(dst, src, nil)
|
||
}
|
||
|
||
// os/file.go:140 — Named return for readability
|
||
func (f *File) Read(b []byte) (n int, err error) {
|
||
if err := f.checkValid("read"); err != nil {
|
||
return 0, err
|
||
}
|
||
n, e := f.read(b)
|
||
return n, f.wrapErr("read", e)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Defer for Resource Cleanup
|
||
|
||
**Pattern name:** `defer mu.Unlock()` / `defer f.Close()`
|
||
|
||
**Source citation:** [net/http/server.go#L3173](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L3173), [net/http/example_handle_test.go#L21](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/example_handle_test.go#L21)
|
||
|
||
**What it does:** Resources acquired at the top of a scope are immediately deferred
|
||
for cleanup. Mutexes are locked then immediately `defer Unlock()`'d.
|
||
|
||
**Why:** Guarantees cleanup regardless of return path (early returns, panics). Keeps
|
||
the acquire/release pair visually adjacent. Reduces bugs from forgotten unlocks.
|
||
|
||
**Anti-pattern:** Manual unlock at each return point; deferring in a loop (deferred
|
||
calls accumulate until function exit); deferring expensive operations that should
|
||
run earlier.
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// net/http/server.go:3173-3174
|
||
func (s *Server) Close() error {
|
||
s.inShutdown.Store(true)
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
// ...
|
||
}
|
||
|
||
// net/http/example_handle_test.go:21-22
|
||
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)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Error Wrapping and Sentinel Errors
|
||
|
||
**Pattern name:** Sentinel Errors + Structured Error Types
|
||
|
||
**Source citation:** [os/error.go#L14](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/error.go#L14), [os/error.go#L46](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/error.go#L46)
|
||
|
||
**What it does:** Package-level sentinel errors (`ErrNotExist`, `ErrPermission`) are
|
||
declared as `var` for use with `errors.Is()`. Structured error types (`*PathError`,
|
||
`*SyscallError`) carry context and implement `Unwrap()` for the errors chain.
|
||
|
||
**Why:** Enables programmatic error handling without string matching. `errors.Is(err, os.ErrNotExist)` works regardless of wrapping depth. Structured types let callers
|
||
extract the operation, path, or underlying syscall error.
|
||
|
||
**Anti-pattern:** Comparing error strings; creating unique error types for every
|
||
possible failure; not implementing `Unwrap`; sentinel errors as `const` (breaks
|
||
`errors.Is` for wrapped errors — use `var`).
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// os/error.go:14-27
|
||
var (
|
||
ErrInvalid = fs.ErrInvalid // "invalid argument"
|
||
ErrPermission = fs.ErrPermission // "permission denied"
|
||
ErrExist = fs.ErrExist // "file already exists"
|
||
ErrNotExist = fs.ErrNotExist // "file does not exist"
|
||
ErrClosed = fs.ErrClosed // "file already closed"
|
||
)
|
||
|
||
// os/error.go:46
|
||
type PathError = fs.PathError
|
||
|
||
// os/error.go:49-57
|
||
type SyscallError struct {
|
||
Syscall string
|
||
Err error
|
||
}
|
||
|
||
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
|
||
func (e *SyscallError) Unwrap() error { return e.Err }
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Receiver Naming: Short, Consistent, Never `this`/`self`
|
||
|
||
**Pattern name:** Single-Letter or Short Receiver Names
|
||
|
||
**Source citation:** All stdlib code; `net/http/server.go` uses `s` for Server, `bufio/scan.go` uses `s` for Scanner
|
||
|
||
**What it does:** Method receivers use 1–2 letter abbreviations of the type name,
|
||
consistent across all methods of that type: `s` for `*Server`, `b` for `*Builder`,
|
||
`f` for `*File`, `t` for `*Timer`.
|
||
|
||
**Why:** Receivers appear on every method. Short names reduce visual noise. Consistency
|
||
within a type avoids confusion. `this`/`self` are alien to Go's conventions.
|
||
|
||
**Anti-pattern:** `this`, `self`, `me`; long receiver names like `server`, `scanner`;
|
||
inconsistent receivers across methods of the same type.
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// net/http/server.go
|
||
func (s *Server) ListenAndServe() error { ... }
|
||
func (s *Server) Serve(l net.Listener) error { ... }
|
||
func (s *Server) Shutdown(ctx context.Context) error { ... }
|
||
|
||
// strings/builder.go
|
||
func (b *Builder) WriteString(s string) (int, error) { ... }
|
||
func (b *Builder) String() string { ... }
|
||
func (b *Builder) Grow(n int) { ... }
|
||
|
||
// os/file.go
|
||
func (f *File) Read(b []byte) (n int, err error) { ... }
|
||
func (f *File) Name() string { ... }
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Constants: Typed, Grouped, with iota
|
||
|
||
**Pattern name:** Typed Constants with iota
|
||
|
||
**Source citation:** [crypto/crypto.go#L70](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/crypto/crypto.go#L70), [time/time.go#L936](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/time/time.go#L936)
|
||
|
||
**What it does:** Related constants are grouped in a `const ( ... )` block using
|
||
a named type and `iota` for sequential values. Constants of the same type
|
||
are exhaustively listed together.
|
||
|
||
**Why:** Type safety (can't accidentally pass an `os.Flag` where a `crypto.Hash` is
|
||
expected). `iota` eliminates magic numbers. Grouping makes the full set visible.
|
||
|
||
**When to Use**
|
||
|
||
**Triggers:**
|
||
- You have a set of related values that represent distinct states or options (status codes, modes, categories)
|
||
- Raw integers would be meaningless to readers (`SetMode(3)` vs `SetMode(ModeAsync)`)
|
||
- You want the type system to prevent passing a "color" where a "direction" is expected
|
||
|
||
**Example — before:**
|
||
```go
|
||
func SetLogLevel(level int) { ... }
|
||
|
||
// Caller:
|
||
SetLogLevel(3) // what does 3 mean?!
|
||
SetLogLevel(-1) // valid? who knows
|
||
```
|
||
|
||
**Example — after:**
|
||
```go
|
||
type LogLevel int
|
||
|
||
const (
|
||
LevelDebug LogLevel = iota
|
||
LevelInfo
|
||
LevelWarn
|
||
LevelError
|
||
)
|
||
|
||
func SetLogLevel(level LogLevel) { ... }
|
||
|
||
// Caller:
|
||
SetLogLevel(LevelWarn) // self-documenting
|
||
```
|
||
|
||
**When NOT to Use**
|
||
|
||
**Don't use typed constants with iota when:**
|
||
- The values have external meaning (HTTP status codes, exit codes, protocol bytes) — use explicit values
|
||
- The set is not exhaustive or will have gaps (iota assigns sequential values; gaps require explicit assignment)
|
||
- You need the constant to interoperate with untyped int APIs without casting everywhere
|
||
|
||
**Over-application example:**
|
||
```go
|
||
type HTTPStatus int
|
||
|
||
const (
|
||
StatusOK HTTPStatus = iota // 0?! HTTP 200 is not 0
|
||
StatusNotFound // 1?! Should be 404
|
||
StatusServerError // 2?! Should be 500
|
||
)
|
||
```
|
||
|
||
**Better alternative:**
|
||
```go
|
||
type HTTPStatus int
|
||
|
||
const (
|
||
StatusOK HTTPStatus = 200
|
||
StatusNotFound HTTPStatus = 404
|
||
StatusServerError HTTPStatus = 500
|
||
)
|
||
```
|
||
|
||
**Why:** `iota` is for sequential enumerations where the actual numeric value doesn't matter (only the distinctness matters). When values have external meaning (wire protocols, HTTP, exit codes), use explicit values.
|
||
|
||
**Anti-pattern:** Untyped numeric constants; separate `const` declarations for related
|
||
values; using raw integers in function signatures.
|
||
|
||
**Code examples from source:**
|
||
|
||
```go
|
||
// crypto/crypto.go:70-85
|
||
const (
|
||
MD4 Hash = 1 + iota
|
||
MD5
|
||
SHA1
|
||
SHA224
|
||
SHA256
|
||
// ...
|
||
)
|
||
|
||
// time/time.go:936-943
|
||
const (
|
||
Nanosecond Duration = 1
|
||
Microsecond = 1000 * Nanosecond
|
||
Millisecond = 1000 * Microsecond
|
||
Second = 1000 * Millisecond
|
||
Minute = 60 * Second
|
||
Hour = 60 * Minute
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Comments: Guard Clauses Over Conditions
|
||
|
||
**Pattern name:** `// guards x` Field Comments
|
||
|
||
**Source citation:** [net/http/example_handle_test.go#L16](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/example_handle_test.go#L16)
|
||
|
||
**What it does:** When a sync primitive (mutex) protects specific fields, a brief
|
||
comment documents what it guards: `mu sync.Mutex // guards n`.
|
||
|
||
**Why:** Concurrency bugs come from unclear ownership. A one-line comment makes the
|
||
lock's scope obvious to every reader.
|
||
|
||
**Anti-pattern:** No documentation of what a lock protects; locks that protect
|
||
"everything" (unclear scope); comments that restate the type.
|
||
|
||
**Code example from source:**
|
||
|
||
```go
|
||
// net/http/example_handle_test.go:16-17
|
||
type countHandler struct {
|
||
mu sync.Mutex // guards n
|
||
n int
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 11. Duration Type Pattern
|
||
|
||
**Pattern name:** Named Type for Semantic Units
|
||
|
||
**Source citation:** [time/time.go#L915](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/time/time.go#L915)
|
||
|
||
**What it does:** `Duration` is `type Duration int64` — a named type over a primitive.
|
||
This gives it its own method set (`String()`, `Hours()`, `Truncate()`) and prevents
|
||
accidental mixing with raw int64 values.
|
||
|
||
**Why:** Semantic meaning through the type system. You can't accidentally pass
|
||
nanoseconds where seconds are expected. Methods provide conversion and formatting.
|
||
Constants like `time.Second` make intent clear.
|
||
|
||
**When to Use**
|
||
|
||
**Triggers:**
|
||
- A primitive type (`int`, `string`, `float64`) has a specific **semantic meaning** in your domain
|
||
- You want to attach methods (formatting, validation, arithmetic) to the value
|
||
- You've seen bugs from accidentally mixing units (`int` could be seconds, milliseconds, or nanoseconds)
|
||
|
||
**Example — before:**
|
||
```go
|
||
func SetTimeout(ms int) { ... } // is this milliseconds? seconds?
|
||
func SetRetries(n int) { ... } // can't accidentally swap these... or CAN you?
|
||
|
||
SetTimeout(5) // 5 what?
|
||
SetRetries(500) // oops, swapped arguments — compiles fine!
|
||
```
|
||
|
||
**Example — after:**
|
||
```go
|
||
type Timeout time.Duration
|
||
type RetryCount int
|
||
|
||
func SetTimeout(t Timeout) { ... }
|
||
func SetRetries(n RetryCount) { ... }
|
||
|
||
SetTimeout(Timeout(5 * time.Second)) // explicit units
|
||
SetRetries(RetryCount(3)) // can't swap — different types
|
||
```
|
||
|
||
**When NOT to Use**
|
||
|
||
**Don't create named types when:**
|
||
- The primitive is only used in one place (over-engineering for a single call site)
|
||
- The type would need constant casting back to the primitive for stdlib interop
|
||
- The semantic meaning is already clear from the parameter name (`func Sleep(seconds int)` in a script is fine)
|
||
|
||
**Over-application example:**
|
||
```go
|
||
type Port uint16
|
||
type Host string
|
||
type Path string
|
||
|
||
// Now every function that takes these needs explicit construction:
|
||
func Connect(h Host, p Port, path Path) { ... }
|
||
Connect(Host("localhost"), Port(8080), Path("/api")) // ceremony for no safety gain
|
||
```
|
||
|
||
**Better alternative:**
|
||
```go
|
||
// For simple configurations, a struct with named fields provides clarity without type ceremony:
|
||
type Endpoint struct {
|
||
Host string
|
||
Port uint16
|
||
Path string
|
||
}
|
||
|
||
func Connect(ep Endpoint) { ... }
|
||
Connect(Endpoint{Host: "localhost", Port: 8080, Path: "/api"})
|
||
```
|
||
|
||
**Why:** Named types shine when you need methods, when confusion between units causes real bugs (seconds vs milliseconds), or when the type system should prevent mixing semantically different values of the same primitive. If you're just adding type annotations to strings that don't interact, you're adding ceremony without safety.
|
||
|
||
**Anti-pattern:** Using raw `int64` for durations; accepting `int` parameters for
|
||
time intervals; mixing units (milliseconds in one place, seconds in another).
|
||
|
||
**Code example from source:**
|
||
|
||
```go
|
||
// time/time.go:915
|
||
type Duration int64
|
||
|
||
// time/time.go:947-949
|
||
func (d Duration) String() string {
|
||
var arr [32]byte
|
||
n := d.format(&arr)
|
||
return string(arr[n:])
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 12. gofmt: Non-Negotiable Formatting
|
||
|
||
**Pattern name:** Canonical Formatting via gofmt
|
||
|
||
**Source citation:** Every file in the Go standard library
|
||
|
||
**What it does:** All Go code is formatted with `gofmt`. Tabs for indentation, spaces
|
||
for alignment. No style debates — the tool decides.
|
||
|
||
**Why:** Eliminates formatting bikesheds. All Go code looks the same regardless of
|
||
author. Diffs show only semantic changes, never style changes. Tooling can parse
|
||
and emit canonical code.
|
||
|
||
**Anti-pattern:** Manual formatting; spaces for indentation; custom alignment rules;
|
||
checking in code that `gofmt` would modify.
|
||
|
||
**Key rules enforced by gofmt:**
|
||
|
||
- Tabs for indentation
|
||
- Opening brace on the same line (`if x {`)
|
||
- No optional parentheses (`if x`, not `if (x)`)
|
||
- Aligned struct field tags
|
||
- One blank line between top-level declarations
|
||
- No trailing whitespace
|
||
|
||
---
|
||
|
||
## 13. Import Organization
|
||
|
||
**Pattern name:** Grouped Imports (stdlib / external / internal)
|
||
|
||
**Source citation:** [net/http/server.go#L9](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L9)
|
||
|
||
**What it does:** Imports are organized in groups separated by blank lines:
|
||
1. Standard library
|
||
2. External packages (golang.org/x, third-party)
|
||
3. Internal packages
|
||
|
||
The `goimports` tool enforces this automatically.
|
||
|
||
**Why:** Scannable at a glance. Makes dependency provenance clear (stdlib vs.
|
||
external). Reduces merge conflicts.
|
||
|
||
**Code example from source:**
|
||
|
||
```go
|
||
// net/http/server.go:9-36
|
||
import (
|
||
"bufio"
|
||
"bytes"
|
||
"context"
|
||
"crypto/tls"
|
||
"errors"
|
||
"fmt"
|
||
// ... more stdlib ...
|
||
"time"
|
||
_ "unsafe" // for linkname
|
||
|
||
"golang.org/x/net/http/httpguts"
|
||
)
|
||
```
|
||
|
||
<!-- PATTERN_COMPLETE -->
|