diff options
author | Sameer Ajmani <sameer@golang.org> | 2015-02-04 08:39:12 -0500 |
---|---|---|
committer | Sameer Ajmani <sameer@golang.org> | 2015-02-04 22:27:29 +0000 |
commit | c2c302715559253d73e5dd6734e11adbd62be96c (patch) | |
tree | 813758330569765a209bb46b097741fab0efc33e | |
parent | aa517ef51cdf3133ac38e38cce03ad79e928cc42 (diff) |
blog: Package names post
Change-Id: Iddb15b375e2e11207105d3373b4c8f77e6dd7633
Reviewed-on: https://go-review.googlesource.com/3843
Reviewed-by: Rob Pike <r@golang.org>
-rw-r--r-- | content/package-names.article | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/content/package-names.article b/content/package-names.article new file mode 100644 index 0000000..7021f1f --- /dev/null +++ b/content/package-names.article @@ -0,0 +1,247 @@ +Package names +4 Feb 2015 +Tags: package, names, style + +Sameer Ajmani + +* Introduction + +Go code is organized into packages. +Within a package, code can refer to any identifier (name) defined within, while +clients of the package may only reference the package's exported types, +functions, constants, and variables. +Such references always include the package name as a prefix: `foo.Bar` refers to +the exported name `Bar` in the imported package named `foo`. + +Good package names make code better. +A package's name provides context for its contents, making it easier for clients +to understand what the package is for and how to use it. +The name also helps package maintainers determine what does and does not belong +in the package as it evolves. +Well-named packages make it easier to find the code you need. + +Effective Go provides +[[https://golang.org/doc/effective_go.html#names][guidelines]] for naming +packages, types, functions, and variables. +This article expands on that discussion and surveys names found in the standard +library. +It also discusses bad package names and how to fix them. + +* Package names + +Good package names are short and clear. +They are lower case, with no `under_scores` or `mixedCaps`. +They are often simple nouns, such as: + +- `time` (provides functionality for measuring and displaying time) +- `list` (implements a doubly linked list) +- `http` (provides HTTP client and server implementations) + +The style of names typical of another language might not be idiomatic in a Go +program. +Here are two examples of names that might be good style in other languages but +do not fit well in Go: + +- `computeServiceClient` +- `priority_queue` + +A Go package may export several types and functions. +For example, a `compute` package could export a `Client` type with methods for +using the service as well as functions for partitioning a compute task across +several clients. + +*Abbreviate*judiciously.* +Package names may be abbreviated when the abbreviation is familiar to the +programmer. +Widely-used packages often have compressed names: + +- `strconv` (string conversion) +- `syscall` (system call) +- `fmt` (formatted I/O) + +On the other hand, if abbreviating a package name makes it ambiguous or unclear, +don't do it. + +*Don't*steal*good*names*from*the*user.* +Avoid giving a package a name that is commonly used in client code. +For example, the buffered I/O package is called `bufio`, not `buf`, since `buf` +is a good variable name for a buffer. + +* Naming package contents + +A package name and its contents' names are coupled, since client code uses them +together. +When designing a package, take the client's point of view. + +*Avoid*stutter.* +Since client code uses the package name as a prefix when referring to the +package contents, the names for those contents need not repeat the package name. +The HTTP server provided by the `http` package is called `Server`, not +`HTTPServer`. +Client code refers to this type as `http.Server`, so there is no ambiguity. + +*Simplify*function*names.* +When a function in package pkg returns a value of type `pkg.Pkg` (or +`*pkg.Pkg`), the function name can often omit the type name without confusion: + + start := time.Now() // start is a time.Time + t, err := time.Parse(time.Kitchen, "6:06PM") // t is a time.Time + +A function named `New` in package `pkg` returns a value of type `*pkg.Pkg`. +This is a standard entry point for client code using that type: + + q := list.New() // q is a *list.List + +When a function returns a value of type `pkg.T`, where `T` is not `Pkg`, the +function name may include `T` to make client code easier to understand. +A common situation is a package with multiple New-like functions: + + d, err := time.ParseDuration("10s") // d is a time.Duration + elapsed := time.Since(start) // elapsed is a time.Duration + ticker := time.NewTicker(d) // ticker is a *time.Ticker + timer := time.NewTimer(d) // timer is a *time.Timer + +Types in different packages can have the same name, because from the client's +point of view such names are discriminated by the package name. +For example, the standard library includes several types named `Reader`, +including `jpeg.Reader`, `bufio.Reader`, and `csv.Reader`. +Each package name fits with `Reader` to yield a good type name. + +If you cannot come up with a package name that's a meaningful prefix for the +package's contents, the package abstraction boundary may be wrong. +Write code that uses your package as a client would, and restructure your +packages if the result seems poor. +This approach will yield packages that are easier for clients to understand and +for the package developers to maintain. + +* Package paths + +A Go package has both a name and a path. +The package name is specified in the package statement of its source files; +client code uses it as the prefix for the package's exported names. +Client code uses the package path when importing the package. +By convention, the last element of the package path is the package name: + + import ( + "fmt" // package fmt + "os/exec" // package exec + "golang.org/x/net/context" // package context + ) + +Build tools map package paths onto directories. +The go tool uses the [[https://golang.org/doc/code.html#GOPATH][GOPATH]] +environment variable to find the source files for path `"github.com/user/hello"` +in directory `$GOPATH/src/github.com/user/hello`. +(This situation should be familiar, of course, but it's important to be clear +about the terminology and structure of packages.) + +*Directories.* +The standard library uses like directories `crypto`, `container`, `encoding`, +and `image` to group packages for related protocols and algorithms. +There is no actual relationship among the packages in one of these directories; +a directory just provides a way to arrange the files. +Any package can import any other package provided the import does not create a +cycle. + +Just as types in different packages can have the same name without ambiguity, +packages in different directories can have the same name. +For example, +[[https://golang.org/pkg/runtime/pprof][runtime/pprof]] provides profiling data +in the format expected by the [[https://code.google.com/p/gperftools][pprof]] +profiling tool, while [[https://golang.org/pkg/net/http/pprof][net/http/pprof]] +provides HTTP endpoints to present profiling data in this format. +Client code uses the package path to import the package, so there is no +confusion. +If a source file needs to import both `pprof` packages, it can +[[https://golang.org/ref/spec#Import_declarations][rename]] one or both locally. +When renaming an imported package, the local name should follow the same +guidelines as package names (lower case, no `under_scores` or `mixedCaps`). + +* Bad package names + +Bad package names make code harder to navigate and maintain. +Here are some guidelines for recognizing and fixing bad names. + +*Avoid*meaningless*package*names.* +Packages named `util`, `common`, or `misc` provide clients with no sense of what +the package contains. +This makes it harder for clients to use the package and makes it harder for +maintainers to keep the package focused. +Over time, they accumulate dependencies that can make compilation significantly +and unnecessarily slower, especially in large programs. +And since such package names are generic, they are more likely to collide with +other packages imported by client code, forcing clients to invent names to +distinguish them. + +*Break*up*generic*packages.* +To fix such packages, look for types and functions with common name elements and +pull them into their own package. +For example, if you have + + package util + func NewStringSet(...string) map[string]bool {...} + func SortStringSet(map[string]bool) []string {...} + +then client code looks like + + set := util.NewStringSet("c", "a", "b") + fmt.Println(util.SortStringSet(set)) + +Pull these functions out of `util` into a new package, choosing a name that fits +the contents: + + package stringset + func New(...string) map[string]bool {...} + func Sort(map[string]bool) []string {...} + +then the client code becomes + + set := stringset.New("c", "a", "b") + fmt.Println(stringset.Sort(set)) + +Once you've made this change, its easier to see how to improve the new package: + + package stringset + type Set map[string]bool + func New(...string) Set {...} + func (s Set) Sort() []string {...} + +which yields even simpler client code: + + set := stringset.New("c", "a", "b") + fmt.Println(set.Sort()) + +The name of the package is a critical piece of its design. +Work to eliminate meaningless package names from your projects. + +*Don't*use*a*single*package*for*all*your*APIs.* +Many well-intentioned programmers put all the interfaces exposed by their +program into a single package named `api`, `types`, or `interfaces`, thinking it +makes it easier to find the entry points to their code base. +This is a mistake. +Such packages suffer from the same problems as those named `util` or `common`, +growing without bound, providing no guidance to users, accumulating +dependencies, and colliding with other imports. +Break them up, perhaps using directories to separate public packages from +implementation. + +*Avoid*unnecessary*package*name*collisions.* +While packages in different directories may have the same name, packages that +are frequently used together should have distinct names. +This reduces confusion and the need for local renaming in client code. +For the same reason, avoid using the same name as popular standard packages like +`io` or `http`. + +* Conclusion + +Package names are central to good naming in Go programs. +Take the time to choose good package names and organize your code well. +This helps clients understand and use your packages and helps maintainers to +grow them gracefully. + +* Further reading + +- [[https://golang.org/doc/effective_go.html][Effective Go]] +- [[https://golang.org/doc/code.html][How to Write Go Code]] +- [[https://blog.golang.org/organizing-go-code][Organizing Go Code (2012 blog post)]] +- [[https://talks.golang.org/2014/organizeio.slide][Organizing Go Code (2014 Google I/O talk)]] |