Organization of code into packages
We use Go packages to delimit units of functionality.
We generally like smaller packages with a tight scope, but not so small that components that are always updated together are pulled away from each other by package boundaries.
When to create new packages
Here are example reasons to create new packages:
New functionality is being added that is self-contained and can be described independently.
Protobuf definitions are being added to a package that did not use protobufs before.
An existing package has grown too large; for example:
it takes too long to run its unit tests.
there are so many components that newcomers become confused; it is generally harder to learn.
making changes to one place require verification to many other places, because of insufficient encapsulation.
another team has expressed interest in reusing a sub-part of the package but they cannot import the entire package.
New functionality requires a separate license (see below for details).
A portion of code uses custom build rules.
A piece of code is identified as a valuable standalone library that can be reused in other projects.
(In that case, we also sometimes elevate it to its own repository.)
Where to create new packages and how to name them
Packages that correspond to sub-components of an existing package can be created as sub-directories.
However, we still prefer package names to be unique across the entire project, because otherwise
goimports
may auto-import the wrong package.
New packages for protobuf definitions should be placed in a
xxxxpb
sub-directory of the parent where they apply. For example,pkg/server/serverpb
,pkg/sql/sessiondatapb
. Protobuf definitions common to multiple components should be located as a sub-package of the nearest common parent package.pkg/ccl
sub-directories: package that define Enterprise features (see explanation below).pkg/cmd
sub-directories: packages that define a standalone program.pkg/util
sub-directories: “utility” packages that arguably can have value across multiple programs (even within the crdb repository).
In case of doubt, ask for recommendations from the nearest TL.
To name a package, we use the following guidelines:
All lower-case. No capitals or underscores.
Choose a name that does not need to be renamed using named imports at most call sites (unambiguous in the most frequent use contexts).
As discussed above, we still prefer package names to be unique across the entire project.
A good trick to make this work is to take the name of a parent package and add a suffix or prefix.
For example,server/serverpb
,kv/kvserver
,util/contextutil
,util/testutil
, etc.
Short and succinct. Remember that the name is identified in full at every call site.
Not plural. For example,
net/url
, notnet/urls
.Not "common", "util", "shared", or "lib". These are bad, uninformative names.
See also Package Names and Style guideline for Go packages.
How to break cyclical dependencies
Sometimes after choosing a package layout, the Go compi