Clean code in Go · 3 MIN READ
Names that expose responsibility
Make a caller understand units, results and ownership from the API.
Make a caller understand units, results and ownership from the API. Names should reduce guesses at the call site. Trace the worked Go example, then compare the benefit with the cost before applying this design to your own project.
Clean Go · A MAP OF THE IDEA
- Caller reads the contract
- Typed input exposes units
- Result makes the decision explicit
Useful when
- Different units or meanings can be confused
- An API is read outside its implementation
Watch out
- Verbosity can hide a simple idea
- Naming cannot fix mixed responsibilities
Implementation & operation cost: Low
The design decision
Names should reduce guesses at the call site. Include units when a number otherwise has several meanings, avoid repeating the package name in every exported identifier, and replace vague helpers with a responsibility such as parsing a duration or calculating a quote.
A complete Go example
Read this program from the entry point and follow the calls shown in the diagram. The output is included at the end so you can check the behavior without a compiler.
package main
import (
"fmt"
"time"
)
type RetryPolicy struct {
Delay time.Duration
MaxAttempts int
}
func NextDelay(policy RetryPolicy, attempt int) (time.Duration, bool) {
if attempt >= policy.MaxAttempts {
return 0, false
}
return policy.Delay, true
}
func main() {
delay, retry := NextDelay(RetryPolicy{Delay: 250 * time.Millisecond, MaxAttempts: 3}, 1)
fmt.Println(delay, retry)
}
// Output:
// 250ms true
Delay has a duration type rather than an undocumented integer unit. NextDelay returns both a value and a decision, so zero is not overloaded to mean stop. Attempt numbering must still be documented: this example uses a zero-based attempt index.
Tradeoffs and the simpler alternative
Choose brevity in small scopes and precision at boundaries. A long name cannot repair a confused responsibility. Keep comments for contracts, assumptions and reasons; let gofmt handle alignment rather than preserving a personal formatting convention.
Name the contract, not the implementation accident
Prefer ReserveStock to Process and OrderID to a bare string whose meaning changes by call site. Include units when a numeric type does not make them clear; use time.Duration rather than an undocumented integer timeout. A name should tell the caller whether a method mutates, blocks or acquires a resource. Avoid repeating a package name in every exported identifier when the qualified call already supplies it. Short names remain useful for small, obvious scopes such as a loop index.
Make invalid states harder to pass
A named type can distinguish CustomerID from OrderID at compile time, but a string alias alone does not validate content. Validate input at the boundary and construct a value that has a documented invariant. Avoid boolean flag arguments whose call-site meaning is unclear; separate operations or a small options type can be easier to read. Do not replace every primitive with a wrapper: choose types where confusing two values or forgetting validation has a real cost.
Review names with a caller and a failure
Read a call without opening its implementation. Can you tell whether the result may be partial, who owns returned bytes and which error means missing? Rename or document the contract when the answer is unclear. Test a malformed ID and an empty value before claiming a constructor makes invalid states impossible. During a rename, keep public compatibility or provide a migration path; internal clarity and external API stability are separate concerns.
TAKE THIS WITH YOU
Reduce ambiguity with types and precise API names. Choose brevity in small scopes and precision at boundaries. A long name cannot repair a confused responsibility. Keep comments for contracts, assumptions and reasons; let gofmt handle alignment rather than preserving a personal formatting convention.
Check your understanding
Which statement best describes Names that expose responsibility?
Go to the source
Progress stays in this browser. No account needed.