blob: 74caf58a69195b489cbfcc18f75b92645a5df3ec [file] [log] [blame]
What's in a name?
October 2014
Andrew Gerrand
Google Inc.
adg@golang.org
@enneff
https://go.dev/
* Names matter
Readability is the defining quality of good code.
Good names are critical to readability.
This talk is about naming in Go.
* Good names
A good name is:
- Consistent (easy to guess),
- Short (easy to type),
- Accurate (easy to understand).
* A rule of thumb
The greater the distance between a name's declaration and its uses,
the longer the name should be.
* Use MixedCase
Names in Go should use `MixedCase`.
(Don't use `names_with_underscores`.)
Acronyms should be all capitals, as in `ServeHTTP` and `IDProcessor`.
* Local variables
Keep them short; long names obscure what the code _does_.
Common variable/type combinations may use really short names:
Prefer `i` to `index`.
Prefer `r` to `reader`.
Prefer `b` to `buffer`.
Avoid redundant names, given their context:
Prefer `count` to `runeCount` inside a function named `RuneCount`.
Prefer `ok` to `keyInMap` in the statement
v, ok := m[k]
Longer names may help in long functions, or functions with many local variables.
(But often this just means you should refactor.)
* Bad
func RuneCount(buffer []byte) int {
runeCount := 0
for index := 0; index < len(buffer); {
if buffer[index] < RuneSelf {
index++
} else {
_, size := DecodeRune(buffer[index:])
index += size
}
runeCount++
}
return runeCount
}
* Good
func RuneCount(b []byte) int {
count := 0
for i := 0; i < len(b); {
if b[i] < RuneSelf {
i++
} else {
_, n := DecodeRune(b[i:])
i += n
}
count++
}
return count
}
* Parameters
Function parameters are like local variables,
but they also serve as documentation.
Where the types are descriptive, they should be short:
func AfterFunc(d Duration, f func()) *Timer
func Escape(w io.Writer, s []byte)
Where the types are more ambiguous, the names may provide documentation:
func Unix(sec, nsec int64) Time
func HasPrefix(s, prefix []byte) bool
* Return values
Return values on exported functions should only be named for documentation purposes.
These are good examples of named return values:
func Copy(dst Writer, src Reader) (written int64, err error)
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
* Receivers
Receivers are a special kind of argument.
By convention, they are one or two characters that reflect the receiver type,
because they typically appear on almost every line:
func (b *Buffer) Read(p []byte) (n int, err error)
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
func (r Rectangle) Size() Point
Receiver names should be consistent across a type's methods.
(Don't use `r` in one method and `rdr` in another.)
* Exported package-level names
Exported names are qualified by their package names.
Remember this when naming exported variables, constants, functions, and types.
That's why we have `bytes.Buffer` and `strings.Reader`,
not `bytes.ByteBuffer` and `strings.StringReader`.
* Interface Types
Interfaces that specify just one method are usually just that function name with 'er' appended to it.
type Reader interface {
Read(p []byte) (n int, err error)
}
Sometimes the result isn't correct English, but we do it anyway:
type Execer interface {
Exec(query string, args []Value) (Result, error)
}
Sometimes we use English to make it nicer:
type ByteReader interface {
ReadByte() (c byte, err error)
}
When an interface includes multiple methods, choose a name that accurately describes its purpose (examples: `net.Conn`, `http.ResponseWriter`, `io.ReadWriter`).
* Errors
Error types should be of the form `FooError`:
type ExitError struct {
...
}
Error values should be of the form `ErrFoo`:
var ErrFormat = errors.New("image: unknown format")
* Packages
Choose package names that lend meaning to the names they export.
Steer clear of `util`, `common`, and the like.
* Import paths
The last component of a package path should be the same as the package name.
"compress/gzip" // package gzip
Avoid stutter in repository and package paths:
"code.google.com/p/goauth2/oauth2" // bad; my fault
For libraries, it often works to put the package code in the repo root:
"github.com/golang/oauth2" // package oauth2
Also avoid upper case letters (not all file systems are case sensitive).
* The standard library
Many examples in this talk are from the standard library.
The standard library is a great place to find good Go code.
Look to it for inspiration.
But be warned:
When the standard library was written, we were still learning.
Most of it we got right, but we made some mistakes.
* Conclusion
Use short names.
Think about context.
Use your judgment.