Before this:Interfaces & composition
Writing idiomatic Go
Key takeaways
Idiomatic Go is remarkably uniform because so much is settled for you. gofmt
formats every file the same way. Functions accept interfaces and return structs.
Errors are handled explicitly, never swallowed. Names are short in short
scopes and clear at package boundaries. The overriding value is clarity over
cleverness — code that reads plainly. Effective Go is the canonical guide.
Go code from different authors looks strikingly similar, and that’s by design. This final craft lesson gathers the conventions that make it so, drawing on everything from interfaces to error handling.
gofmt ends the style debate
There is one correct formatting, and gofmt (or go fmt) produces it — indentation,
alignment, import grouping, all mechanical. Nobody argues about braces in Go because
the tool decides. Run it on save; CI checks it. Formatting is simply not a thing you
think about.
Accept interfaces, return structs
The guiding shape for APIs: take the smallest interface you need as a parameter, return a concrete type:
// Accepts any reader — file, socket, buffer.
func Decode(r io.Reader) (*Frame, error) { ... }
// Returns a concrete *Receiver, not an interface — callers get everything.
func NewReceiver(rate int) *Receiver { ... }
Accepting an interface makes the function flexible for callers; returning a struct gives them the full type without guessing what interface to satisfy. This is the single most Go-shaping habit for library code.
Handle every error, explicitly
Idiomatic Go never ignores an error silently. Handle it, wrap it, or return it —
_ = f() on an error is a code smell:
if err := cfg.Validate(); err != nil {
return fmt.Errorf("invalid config: %w", err)
}
The verbosity is the point: every failure path is visible on the page, which is why Go programs tend to fail informatively rather than crash mysteriously.
Names: short scope, short name
Go names scale with scope. A loop index is i; a receiver is one or two letters; a
package-level exported function has a full, clear name. Long names in tiny scopes are
noise.
| Scope | Convention | Example |
|---|---|---|
| Loop / short block | Single letters | i, r, s |
| Method receiver | 1–2 letters, consistent | func (r *Receiver) |
| Package function/type | Clear, no stutter | scanner.New, not scanner.NewScanner |
| Exported identifier | CamelCase, documented | Demodulate, FreqHz |
Note the anti-stutter rule: it’s bytes.Buffer, not bytes.BytesBuffer — the package
name already carries context.
Clarity over cleverness
The through-line of every rule above is that Go optimizes for the reader. Prefer the plain loop to the clever one-liner; prefer an obvious name to a witty one; prefer a few more lines to a dense trick. Code is read far more than it’s written, and Go’s culture — captured in Effective Go and the Go Code Review Comments — bakes that into how the whole community writes. That shared taste is what lets you drop into GopherTrunk, or any Go project, and read it in the next lesson as if you’d written it.
Quick check: what does "accept interfaces, return structs" advise?
Recap
gofmtsettles all formatting — style is never a debate.- Accept interfaces, return structs for flexible, honest APIs.
- Handle every error explicitly — never swallow it.
- Names are short in short scopes, clear at boundaries, and avoid stutter.
- Above all, favour clarity over cleverness — see Effective Go.
Next up: putting it all together by reading a real Go codebase — GopherTrunk itself.
Frequently asked questions
What does "accept interfaces, return structs" mean?
A function should take the smallest interface it needs as a parameter, so any type with those methods can be passed in — maximum flexibility for callers. But it should return a concrete struct, so callers get the full type with all its methods and fields, not a narrowed-down interface. Be liberal in what you accept, specific in what you return.
Is there an official Go style guide?
gofmt is the style guide — it settles all formatting mechanically, so there are no arguments about braces or spacing. For the deeper conventions, Effective Go and the Go Code Review Comments wiki are the canonical references the whole community follows.