CHAPTER 19 · ORGANIZING CODE

Naming Conventions & Idiomatic Style

Go has very few naming rules baked into the compiler, but the community has strong conventions, enforced by tools and code review. Follow them and your code will look like every other Go project. Ignore them and you'll stand out for the wrong reason.

Learning objectives

  • Use MixedCase (camelCase/PascalCase), never snake_case.
  • Capitalize acronyms correctly (URL, HTTP, ID).
  • Choose short, descriptive names for locals and short receiver names for methods.
  • Pick idiomatic package and interface names.
  • Let gofmt, go vet, and staticcheck do style enforcement for you.

Principles

  • Short, not cryptic. i for an index in a loop is fine; i for an invoice object is not.
  • Scope matters. Shorter names for shorter scopes. A two-line local can be n; a function argument used throughout should be name.
  • Consistency beats novelty. If the stdlib calls it Reader, don't invent IReadable.

Identifier casing

KindStyleExample
Exported (public)PascalCaseUserID, ServeHTTP, MaxRetries
UnexportedcamelCaseuserID, serveHTTP, maxRetries
ConstantsSame as above, just because it's const doesn't make it SHOUTYMaxRetries, not MAX_RETRIES
!
No snake_case, no SHOUT_CASE Go doesn't use underscores in identifiers (except for the blank _). You'll see MAX_RETRIES in C, Python, Ruby, never in Go.

Acronyms are all one case

// Right
URL, ID, HTTP, JSON, DB, API, OS
ServeHTTP, ReadJSON, UserID, APIKey, parseURL

// Wrong
Url, Id, Http, Json   // mixed within an acronym

This applies to both exported and unexported: userID, parseURL. The rule: if a word is an acronym, keep it in one case throughout (all upper for exported, all lower for unexported-at-start).

Package names

  • Lower case, no underscores: strings, nethttp (not net_http).
  • Short and on-topic: time, io, os: not timeutil_helpers.
  • Singular: util, not utils; store, not stores.
  • Names appear as pkg.Name at call sites, so avoid redundancy: strings.Reader, not strings.StringReader.

Interface names

Single-method interfaces are named after their method + "er":

type Reader interface   { Read(p []byte) (n int, err error) }
type Writer interface   { Write(p []byte) (n int, err error) }
type Closer interface   { Close() error }
type Stringer interface { String() string }

Multi-method interfaces get a descriptive name: ReadWriter, http.ResponseWriter. Avoid Hungarian-notation prefixes (IReader), Go doesn't distinguish interface names from type names at the call site.

Receiver names (for methods)

Short, consistent across all methods of a type, typically one or two letters:

func (u *User) FullName() string { ... }
func (u *User) Email() string    { ... }
// Consistent: always `u` for User

Do NOT use this or self. They're idiomatic in Java/Python; in Go they look out of place.

Error naming

Error variables start with Err:

var ErrNotFound    = errors.New("not found")
var ErrUnauthorized = errors.New("unauthorized")

Error types end with Error:

type ValidationError struct { Field, Reason string }
func (e *ValidationError) Error() string { ... }

Constants

const (
    StatusOK       = 200        // PascalCase for exported
    defaultTimeout = 30         // camelCase for unexported
)

Reserved words

Just 25, which is remarkable, compare to Java's ~50 or Python's ~35:

break    default     func   interface  select
case     defer       go     map        struct
chan     else        goto   package    switch
const    fallthrough if     range      type
continue for         import return     var

Let the tools keep you honest

  • gofmt: formatting. You already run it on save (Chapter 4). Non-negotiable.
  • go vet: catches common mistakes (misspellings, bad Printf args, unused results, impossible interface assertions). Run in CI.
  • staticcheck: third-party, but the community standard. Finds style issues, anti-patterns, and real bugs that go vet misses.
    go install honnef.co/go/tools/cmd/staticcheck@latest
    staticcheck ./...
  • golangci-lint: meta-linter that runs many analyzers at once. Most production projects use it.

Check your understanding

Practice exercises

EXERCISE 1

Rename for style

The following has every naming mistake in the book. Rewrite it in idiomatic Go.

type user_account struct {
    User_ID    int
    User_Email string
    CREATED_AT int64
}

func (this *user_account) get_full_email() string {
    return this.User_Email
}

const MAX_USER_NAME_LENGTH = 64
var errorNotFound = "not found"
Show solution
type UserAccount struct {
    ID        int
    Email     string
    CreatedAt int64
}

func (u *UserAccount) FullEmail() string {
    return u.Email
}

const MaxUserNameLength = 64
var ErrNotFound = errors.New("not found")

Key changes: no underscores, consistent camelCase/PascalCase, acronym-case (ID), short receiver (u), Err-prefix for error variable, and the error is an actual error value (not a string).

Further reading

Style in hand.