← Files GophersARCHIVED FILE
skills/go-naming/references/functions-and-options.md
4.63 KB · Oct 4, 2026 · 12:30 UTC
# Functions, Methods, Variants, and Options
## Constructors
Pick one rule per package:
- **One primary type** → `New()`. Callers: `client.New(addr)`.
- **Multiple constructible types** → `NewThing()`. Callers: `http.NewRequest`, `http.NewServeMux`.
```go
// package apiclient — one primary type
func New(addr string) *Client { ... }
// package http — multiple types
func NewRequest(method, url string, body io.Reader) (*Request, error)
func NewServeMux() *ServeMux
```
Stuttering check: `apiclient.NewClient()` repeats the type name; `apiclient.New()` does not.
Constructors that take many parameters should switch to functional options (see below) or to an options struct.
## Getters and Setters
Go omits `Get`. The field accessor is named after the field itself.
```go
// Good
func (u *User) Name() string { return u.name }
func (u *User) SetName(s string) { u.name = s }
// Bad
func (u *User) GetName() string // C# / Java style
```
Reason: `user.Name()` already reads as "the user's name". Adding `Get` repeats the article.
Boolean predicates keep their `Is`/`Has`/`Can` prefix — those are not getters, they are questions.
```go
func (u *User) IsAdmin() bool // not Admin()
func (u *User) HasRole(r string) bool
```
## Format Function Suffix
Functions that take a `fmt`-style format string end in `f`. The suffix is a contract: "format args follow".
```go
fmt.Errorf("parsing %s: %w", path, err)
log.Printf("connecting to %s", addr)
errors.Wrapf(err, "user %d", id) // hypothetical
```
A function named `Wrap(err, "user")` without `f` should not take format arguments.
## Variant Suffixes and Prefixes
Go encodes common variants in the name. These read instantly to anyone who has read enough stdlib.
| Variant | Convention | Example |
|---|---|---|
| Takes a context | `WithContext` suffix | `db.QueryContext`, `http.NewRequestWithContext` |
| Mutates in place | `In` suffix | `slices.SortFunc` (returns sorted copy historically called `Sort`); `Reverse` returning a new slice vs `ReverseIn` mutating |
| Panics on error | `Must` prefix | `template.Must`, `regexp.MustCompile` |
| Returns reader | `NewReader` | `bytes.NewReader`, `strings.NewReader` |
`Must*` is appropriate only when the caller is initialising a package-level variable that *cannot* fail (compiled regex, parsed template). Never use `Must*` for runtime input.
## Functional Options
When a constructor needs many optional knobs, expose `Option` and `WithXxx` helpers.
```go
type Option func(*Server)
func WithLogger(l *slog.Logger) Option {
return func(s *Server) { s.log = l }
}
func WithReadTimeout(d time.Duration) Option {
return func(s *Server) { s.readTimeout = d }
}
func New(addr string, opts ...Option) *Server {
s := &Server{addr: addr, log: slog.Default(), readTimeout: 30 * time.Second}
for _, opt := range opts {
opt(s)
}
return s
}
```
Naming rules:
- The option type is `Option` (not `ServerOption` — `server.Option` already qualifies it).
- Each helper is `WithXxx` matching the conceptual setting (`WithLogger`, `WithReadTimeout`).
- Avoid mixing `WithX`, `SetX`, `UseX`, `EnableX` — pick `With*` and stick to it.
## Named Return Values
Named returns are **documentation, not control flow**. They show up in godoc and clarify what `(int, int)` means.
```go
func Split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // OK in a 3-line function
}
```
Rules:
- Use names when the return tuple is otherwise ambiguous (`(int, int, error)` → `(n, total int, err error)`).
- Avoid naked returns in functions over ~15 lines — readers should not have to scroll back to find what is being returned.
- Do not introduce named returns *just* so you can write `return` instead of `return x, y` — clarity is more important than two saved tokens.
## Test Function Names
```go
func TestParseToken(t *testing.T) // unit
func TestParseToken_InvalidInput(t *testing.T) // subtest variant (underscore allowed)
func BenchmarkParseToken(b *testing.B)
func ExampleParseToken()
```
Subtest case names inside `t.Run(...)` are fully lowercase phrases:
```go
t.Run("valid id", ...)
t.Run("empty input", ...)
t.Run("nil body", ...)
```
## Anti-Patterns
- `GetURL()` instead of `URL()`.
- `URLer`, `Parserer` — `-er` on multi-syllable nouns reads badly; pick a behaviour name.
- `WrapError(err, format, args...)` without `f` suffix — should be `Wrapf`.
- Functional options that take pointers to the option type — keep them simple closures.
- Mixing `Sort`, `SortIn`, `SortFunc`, `SortByKey` in one package without a clear naming axis.
- `MustOpenFile(path)` for runtime user input — panics belong to package init, not request paths.
SHA-256: 141744cc2980555343635d9dd0ca615d43f01fbbef2def8bb684f2aaf51a1c58