The foundations of a Go Program

Let's dive into one of the foundations of Go programs. In this lesson, you'll learn to organize your code in a directory structure that will help separate concerns, promote understanding, and support the evolution of the code.

The importance of directory structure

Command-line programs can be tiny or very large. Large programs have a lot of code and are developed over a long time by many different developers. The proper directory structure can contribute a lot to the program’s long-term success. There are several dimensions that benefit from deliberate and well-planned directory structure.

Code organization

The code of every large program should be organized in a way that makes it easy to locate, search, and wrap your head around. Directories with specific roles are a time-tested strategy to achieve this.

Developer collaboration

Developers can collaborate more easily when specific aspects of the program live in distinct directories. Many types of changes will involve specific directories, while other cross-functional changes will involve multiple directories. It is convenient to review changes when files that perform similar functions live in the same directory. Developers can detect omissions and changes that don’t follow consistent patterns more easily.

Program evolution

As the program grows, directories and subdirectories help to extend the program without making the overall structure represented by directories more complicated. A directory with ten files is not much more complicated than a directory that contains a hundred files.

Go workspaces, $GOPATH, and modules

Before Go 1.11, Go programs had to live in a very strict hierarchy called a workspace. The $GOPATH environment variable had a list of one or more directories, and three sub-directories under the $GOPATH:

  • src
  • bin
  • pkg

All your source code and the source code of your dependencies would go under source typically organized further by the source control system it originated from. Compiled packages would go under pkg and compiled executables would go under bin.

Go 1.11 introduced preliminary support, with a special environment variable, for modules where your code could be anywhere as long as it contained a go.mod file. Go 1.12 supported modules by default anywhere except inside $GOPATH. Go 1.13 supports modules anywhere, including inside GOPATH.

For more information about Go modules, check out https://github.com/golang/go/wiki/Modules.

The elements of a Go program

There are many ways to divide and categorize the files that go into a Go program. I highlight some of the most common ones.

Packages

Go code is organized in packages. A package is a directory that contains files and sub-packages, or sub-directories. Here is the repo_manager package:

$ tree repo_manager/
repo_manager/
├── repo_manager.go
├── repo_manager_suite_test.go
└── repo_manager_test.go

The repo_manager lives under pkg/repo_manager and has three Go files.

Code in a package has access to all the variables and functions defined in the package. Meanwhile, code in other packages has access only to public variables and functions with capitalized names. Every source code file must have a package statement at the top that matches the package name.

package repo_manager

...

Commands

Commands are executables. In a command-line program the program itself is a command. A command will have a file that contains the main() function. If a project has multiple executables, there will be multiple commands (possibly in multiple sub-directories) that contain the main() function. Each one will have to be built separately. They can all import the same packages, but it’s not recommended to import other commands. If there is shared functionality useful for multiple commands, it should be placed in a package both commands can import.

Tests

Tests are critical for any program, and command-line programs are no different. Go recognizes this fact and provides support for creating and running tests out of the box via the standard testing package and the go test command. If a file has a name with the suffix _test.go, Go will consider it a test file and run the tests in it when you type go test. We will discuss testing in detail later in the course.

For-example, the repo_manager package, has a test called repo_manager_test.go.

Documentation

Programs often have documentation beyond comments in the code. In particular, command-line programs usually have detailed in-program help about each command and option. However, some programs have external documentation, too.

Metadata and build information

Here, you’ll often find all kinds of files such as README, LICENSE, Dockerfile, go.mod, etc. Those files are not Go source files, but they support the program in various ways. In particular, the go.mod file describes your program’s dependencies.

Get hands-on with 1200+ tech skills courses.