blob: 0cff214f5bf2d79ecde04373e9aefa01da143b3d [file] [log] [blame]
Organizing Go code
David Crawshaw
* Packages
* Go programs are made up of packages
All Go source is part of a package.
Every file begins with a package statement.
Programs start in package main.
.play organizeio/hello.go
For very small programs, `main` is the only package you need to write.
The hello world program _imports_ package `fmt`.
The function `Println` is defined in the fmt package.
* An example package: fmt
// Package fmt implements formatted I/O.
package fmt
// Println formats using the default formats for its
// operands and writes to standard output.
func Println(a ...interface{}) (n int, err error) {
func newPrinter() *pp {
The `Println` function is _exported_. It starts with an upper case
letter, which means other packages are allowed to call it.
The `newPrinter` function is _unexported_. It starts with a lower
case letter, so it can only be used inside the fmt package.
* The shape of a package
Packages collect related code.
They can be big or small,
and may be spread across multiple files.
All the files in a package live in a single directory.
The `net/http` package exports more than 100 names. (18 files)
The `errors` package exports just one. (1 file)
* The name of a package
Keep package names short and meaningful.
Don't use underscores, they make package names long.
- `io/ioutil` not `io/util`
- `suffixarray` not `suffix_array`
Don't overgeneralize. A `util` package could be anything.
The name of a package is part of its type and function names.
On its own, type `Buffer` is ambiguous. But users see:
buf := new(bytes.Buffer)
Choose package names carefully.
Choose good names for users.
* The testing of a package
Tests are distinguished by file name. Test files end in `_test.go`.
package fmt
import "testing"
var fmtTests = []fmtTest{
{"%d", 12345, "12345"},
{"%v", 12345, "12345"},
{"%t", true, "true"},
func TestSprintf(t *testing.T) {
for _, tt := range fmtTests {
if s := Sprintf(tt.fmt, tt.val); s != tt.out {
Test well.
* Code organization
* Introducing workspaces
Your Go code is kept in a _workspace_.
A workspace contains _many_ source repositories (git, hg).
The Go tool understands the layout of a workspace.
You don't need a `Makefile`. The file layout is everything.
Change the file layout, change the build.
* Let's make a workspace
mkdir /tmp/gows
The `GOPATH` environment variable tells the Go tool where your workspace is located.
go get
The `go` `get` command fetches source repositories from the internet and places them in your workspace.
Package paths matter to the Go tool. Using ""
means the tool knows how to fetch your repository.
go install
The go install command builds a binary and places it in `$GOPATH/bin/fixhub`.
* Our workspace
bin/fixhub # installed binary
pkg/darwin_amd64/ # compiled archives
src/ # source repositories
oauth # used by package go-github
golang/lint/... # used by package fixhub
google/go-github/... # used by package fixhub
cmd/fixhub/fixhub.go # package main
`go` `get` fetched many repositories.
`go` `install` built a binary out of them.
* Why prescribe file layout?
Using file layout for builds means less configuration.
In fact, it means no configuration.
No `Makefile`, no `build.xml`.
Less time configuring means more time programming.
Everyone in the community uses the same layout.
This makes it easier to share code.
The Go tool helps build the Go community.
* Where's your workspace?
It is possible to have multiple workspaces, but most people just use one.
So where do you point your `GOPATH`? A common preference:
.image organizeio/home.png
This puts `src`, `bin`, and `pkg` directories in your home directory.
(Convenient, because `$HOME/bin` is probably already in your `PATH`.)
* Working with workspaces
Unix eschews typing:
$ cd dsymonds/fixhub
$ cd goauth2
A shell function for your `~/.profile`:
gocd () { cd `go list -f '{{.Dir}}' $1` }
This lets you move around using the Go tool's path names:
$ gocd .../lint
* The Go tool's many talents
$ go help
Go is a tool for managing Go source code.
go command [arguments]
The commands are:
Worth exploring! Some highlights:
build compile packages and dependencies
get download and install packages and dependencies
install compile and install packages and dependencies
test test packages
There are more useful subcommands. Check out `vet` and `fmt`.
* Dependency management
* In production, versions matter.
`go` `get` always fetches the latest code, even if your build breaks.
.image organizeio/gogetversion.png
That's fine when developing. It's not fine when releasing.
We need other tools.
* Versioning
My favorite technique: vendoring.
For building binaries, import the packages you care about
into a `_vendor` workspace.
For building libraries, import the packages you care about
into your repository. Rename the imports to:
import ""
Long paths, but trivial to automate. Write a Go program!
Another technique: [[][]], provides versioned package paths: -> (branch/tag v3, v3.N, or v.3.N.M)
* Naming
* Names matter
Programs are full of names. Names have costs and benefits.
*Costs*: *space* *and* *time*
Names need to be in short term memory when reading code.
You can only fit so many. Longer names take up more space.
*Benefits:* *information*
A good name is not only a referent, it conveys information.
Use the shortest name that carries the right amount of information in its context.
Devote time to naming.
* Name style
Use `camelCase`, `not_underscores`.
Local variable names should be short, typically one or two characters.
Package names are usually one lowercase word.
Global variables should have longer names.
Don't stutter.
- `bytes.Buffer` not `bytes.ByteBuffer`
- `zip.Reader` not `zip.ZipReader`
- `errors.New` not `errors.NewError`
- `r` not `bytesReader`
- `i` not `loopIterator`
* Doc comments
Doc comments precede the declaration of an exported identifier:
// Join concatenates the elements of elem to create a single string.
// The separator string sep is placed between elements in the resulting string.
func Join(elem []string, sep string) string {
The godoc tool extracts such comments and presents them on the web:
.image organizeio/godoc.png
* Writing doc comments
Doc comments should be English sentences and paragraphs.
They use no special formatting beyond indentation for preformatted text.
Doc comments should begin with the noun they describe.
// Join concatenates… good
// This function… bad
Package docs go above the package declaration:
// Package fmt…
package fmt
Read the world's Go docs on [[]]. E.g.
* Questions?