You went through the ‘Tour of Go’, played with https://play.golang.org/ and you feel you are ready to write some code. Great! However, you are not sure how to structure your projects. Can you put your code anywhere you want? Is there a standard way to organize your code? What if you need to have multiple application binaries? What does it mean to be ‘go gettable’? These are some of the questions you’ll be asking yourself.
First, you have to understand Go workspaces. ‘How to Write Go Code’ is a good place to start. By default, Go keeps and expects all code in a single workspace. This place is identified by the
GOPATH environment variable. What does it mean for you? It means you have to put your code in the default workspace or you have to change the
GOPATH variable to point to your own location. Either way the actual source code for your project needs to be placed in the
src subdirectory (e.g.,
$GOPATH/src/github.com/your_github_username/your_project). Technically your project doesn’t have to be in a workspace if you don’t import external packages and you use relative imports for your own code, but it’s not recommended. It’s fine for a toy project or a PoC though. Go v1.11 does introduce the concept of modules that allows you to have your project code outside of your
GOPATHwithout the import restrictions mentioned above, but it’s still an experimental feature at this point in time.
You have your project directory in the right place. What’s next?
For a PoC or a very small project where you are the only one writing the code using a single
main.go file in the root directory for your project is enough. If you know your project will be large enough or it’ll go into production and others will be contributing to it you should consider adopting, at least, some of the project layout patterns outlined here.
There are a number of project layout patterns emerging in the Go ecosystem. The two most common patterns are the
pkg directories. You should adopt these patterns unless you have a tiny project.
cmd layout pattern is very useful when you need to have more than one application binary. Each binary gets a subdirectory (e.g.,
your_project/cmd/your_app). This patterns also helps you keep your project/package ‘go gettable’. What does it mean? It means you can use the
go get command to fetch (and install) your project, its applications and its libraries (e.g.,
go get github.com/your_github_username/your_project/cmd/appxg). You don’t have to separate the application files. You’ll be able to build each application with the right set of
go build flags, but
go get will no longer work because it will not know which application code to build. The official Go tools is one example of the
cmd layout patter. A number of other well known projects use the same pattern: Kubernetes, Docker, Prometheus, Influxdb.
pkg layout pattern is also pretty popular. For new Go developers it’s one of the most confusing package structure concepts because Go workspaces have a directory with the same name and that directory has a different purpose (it’s used to store object files for the packages the Go compiler builds). The
pkg directory is where you put your public libraries. They can be used internally by your application. They can also be used by external projects. This is an informal contract between you and other external users of your code. Other projects will import these libraries expecting them to work, so think twice before you put something here. Many well known projects use this pattern: Kubernetes, Docker, Grafana, Influxdb, Etcd.
Some of the libraries in the
pkg directory are not always for public use. Why is that? It happens because many existing Go projects predate the ability to hide internal packages. Some projects put those internal libraries in the
pkg directory to be consistent with the rest of their code structure. Other projects put their internal libraries into separate directories outside of the
pkg directory. Go 1.4 introduce an ability to hide code using
internal directories. What does it mean? If you put your code in an ‘internal’ directory no external project will be able to import that code. Even other code in your project won’t be able to access this internal code if it lives outside of its parent directory. This feature is not widely used yet because it’s relatively new; however, it’s extremely valuable as an additional layer of control (in addition to the lowercase and uppercase function visibility rules in Go). A number of new and well known projects use this pattern: Dep, Docker, Nsq, Go Ethereal, Contour.
internal directory is the place to put your private packages. You can optionally add additional structure by separating your internally shared libraries (e.g.,
your_project/internal/pkg/your_private_lib) and the application code you don’t want others to import (e.g.,
your_project/internal/app/your_app). When you put all of you private code in the ‘internal’ directory the application code in the
cmd directory will be limited to small files that define the ‘main’ function for the corresponding application binaries. Everything else will be imported from the
pkg directories (ark, from Heptio, and loki, from Grafana, are good examples of this
tiny main package pattern).
What if you forked and modified a piece of an external project? Some projects put that code in the
pkg directory, but it’s better to put it in the
third_party top level directory to keep your code separate from the code you borrowed from others.
What about the external packages you import in your projects? Where do they go? You have several options. You can keep them outside of your project. The packages you install with
go get will be saved in your Go workspace. It works most of the times, but depending on the package it might be brittle and unpredictable because when somebody else tries to build your project they might get a backward incompatible version of that package. The solution is ‘vendoring’. With ‘vendoring’ you freeze your dependencies by committing them with your project. Go 1.6 introduced a standard way to ‘vendor’ external packages (it was an experimental feature in Go 1.5). Put your external package in the
vendor directory. How is this different from the
third_party directory? If you import and use external code as-is then it should go into the
vendor directory. If you are using a modified version of an external project then put it in the
If you want to learn more about the project structure used by other Go projects read the ‘Analysis of the Top 1000 Go Repositories’. It’s a little dated, but it’s still useful.
A real project will have additional directories too. You can use this layout template as a starting point for your Go projects: https://github.com/golang-standards/project-layout. It covers the Go project layout patterns described in this blog post and it includes a number of supporting directories you’ll need to have.
Now it’s time to write some code! If you don’t have Go installed take a look at this quick setup guide for Mac OS X (setup on other platforms is similar). Go through the ‘Tour of Go’ if you haven’t done it yet and then read ’50 Shades of Go’ to learn about the most common gotchas in Go, which will save you quite a bit of time when you start writing and debugging code.