Go code generally adheres to the best practices of the Go community. All Go code
is formatted using goimports to ensure both adherence with gofmt's output,
as well as unified organization of imports.
Go code is wrapped at 80 characters per line as a general rule, though some lines may be left longer at the discretion of the developers for clarity. No line should exceed 120 characters in length. When wrapping function calls, each parameter should be on its own line.
We vet and lint our code at commit-time. All code should pass both go vet and
go lint in their default configurations.
Two major deviations from typical Go practices that are worth calling out:
-
We do not prefix commit messages with the packages touched by the commit. The commit includes diffs, diffs include paths, paths imply packages. We consider this unnecessary and noisy.
-
We discourage single-letter variable names and related extra-shortness. Short variable names produce diffs that are more difficult to analyze quickly, and generally result in lower clarity for less experienced developers. We consider this an antipattern, and the additional typed characters to be comparatively very cheap. We make a few exceptions:
- External packages
-
We use the package name irrespective of our own practices, unless it introduces ambiguity.
- The
errvariable -
We use
erras is common in Go. - Iteration indices
-
i,j, etc are well-understood and not domain-specific. - Method receivers
-
Since Go does not have a standard
this, we do use the first letter of each word as the variable name of a method receiver (e.g., for a typeMyCoolType, the method receiver would be namedmct). - Name clashes
-
If a variable name would clash with the name of a type, e.g. a variable of type
magicwhose name would naturally bemagic, the last vowel may be dropped to achieve a unique name.
There are a few common patterns for testing in the Keep codebase:
-
Table-driven tests. Any case where a function needs to verify multiple cases are generally structured as table-driven tests. There is a very short page on table-driven testing on the Go wiki.
-
Carefully aligned error output. Generally all tests that call
Errorfshould provide a clear expected-vs-actual output stacked and aligned to make it trivial to spot differences. This is typically represented as a call like:t.Errorf("\nexpected: [%v]\nactual: [%v]\n", expected, actual). This aligns the expected and actual on top of each other. The context of the error should be clear from the test or subtest’s name, but if some additional information is deemed useful, it can be included before the initial newline. Seepkg/internal/testutilsfor an example of an assert helper that uses this style.