Organizing a Go module
A common question developers new to Go have is “How do I organize my Go project?”, in terms of the layout of files and folders. The goal of this document is to provide some guidelines that will help answer this question. To make the most of this document, make sure you’re familiar with the basics of Go modules by reading the tutorial and managing module source.
Go projects can include packages, command-line programs or a combination of the two. This guide is organized by project type.
Basic package
A basic Go package has all its code in the project’s root directory. The project consists of a single module, which consists of a single package. The package name matches the last path component of the module name. For a very simple package requiring a single Go file, the project structure is:
project-root-directory/
go.mod
modname.go
modname_test.go
[throughout this document, file/package names are entirely arbitrary]
Assuming this directory is uploaded to a GitHub repository at
github.com/someuser/modname
, the module
line in the go.mod
file should say
module github.com/someuser/modname
.
The code in modname.go
declares the package with:
package modname
// ... package code here
Users can then rely on this package by import
-ing it in their Go code with:
import "github.com/someuser/modname"
A Go package can be split into multiple files, all residing within the same directory, e.g.:
project-root-directory/
go.mod
modname.go
modname_test.go
auth.go
auth_test.go
hash.go
hash_test.go
All the files in the directory declare package modname
.
Basic command
A basic executable program (or command-line tool) is structured according to its
complexity and code size. The simplest program can consist of a single Go file
where func main
is defined. Larger programs can have their code split across
multiple files, all declaring package main
:
project-root-directory/
go.mod
auth.go
auth_test.go
client.go
main.go
Here the main.go
file contains func main
, but this is just a convention. The
“main” file can also be called modname.go
(for an appropriate value of
modname
) or anything else.
Assuming this directory is uploaded to a GitHub repository at
github.com/someuser/modname
, the module
line in the go.mod
file should
say:
module github.com/someuser/modname
And a user should be able to install it on their machine with:
$ go install github.com/someuser/modname@latest
Package or command with supporting packages
Larger packages or commands may benefit from splitting off some functionality
into supporting packages. Initially, it’s recommended placing such packages into
a directory named internal
;
this prevents other
modules from depending on packages we don’t necessarily want to expose and
support for external uses. Since other projects cannot import code from our
internal
directory, we’re free to refactor its API and generally move things
around without breaking external users. The project structure for a package is
thus:
project-root-directory/
internal/
auth/
auth.go
auth_test.go
hash/
hash.go
hash_test.go
go.mod
modname.go
modname_test.go
The modname.go
file declares package modname
, auth.go
declares package auth
and so on. modname.go
can import the auth
package as follows:
import "github.com/someuser/modname/internal/auth"
The layout for a command with supporting packages in an internal
directory is
very similar, except that the file(s) in the root directory declare package main
.
Multiple packages
A module can consist of multiple importable packages; each package has its own directory, and can be structured hierarchically. Here’s a sample project structure:
project-root-directory/
go.mod
modname.go
modname_test.go
auth/
auth.go
auth_test.go
token/
token.go
token_test.go
hash/
hash.go
internal/
trace/
trace.go
As a reminder, we assume that the module
line in go.mod
says:
module github.com/someuser/modname
The modname
package resides in the root directory, declares package modname
and can be imported by users with:
import "github.com/someuser/modname"
Sub-packages can be imported by users as follows:
import "github.com/someuser/modname/auth"
import "github.com/someuser/modname/auth/token"
import "github.com/someuser/modname/hash"
Package trace
that resides in internal/trace
cannot be imported outside this
module. It’s recommended to keep packages in internal
as much as possible.
Multiple commands
Multiple programs in the same repository will typically have separate directories:
project-root-directory/
go.mod
internal/
... shared internal packages
prog1/
main.go
prog2/
main.go
In each directory, the program’s Go files declare package main
. A top-level
internal
directory can contain shared packages used by all commands in the
repository.
Users can install these programs as follows:
$ go install github.com/someuser/modname/prog1@latest
$ go install github.com/someuser/modname/prog2@latest
A common convention is placing all commands in a repository into a cmd
directory; while this isn’t strictly necessary in a repository that consists
only of commands, it’s very useful in a mixed repository that has both commands
and importable packages, as we will discuss next.
Packages and commands in the same repository
Sometimes a repository will provide both importable packages and installable commands with related functionality. Here’s a sample project structure for such a repository:
project-root-directory/
go.mod
modname.go
modname_test.go
auth/
auth.go
auth_test.go
internal/
... internal packages
cmd/
prog1/
main.go
prog2/
main.go
Assuming this module is called github.com/someuser/modname
, users can now both
import packages from it:
import "github.com/someuser/modname"
import "github.com/someuser/modname/auth"
And install programs from it:
$ go install github.com/someuser/modname/cmd/prog1@latest
$ go install github.com/someuser/modname/cmd/prog2@latest
Server project
Go is a common language choice for implementing servers. There is a very large variance in the structure of such projects, given the many aspects of server development: protocols (REST? gRPC?), deployments, front-end files, containerization, scripts and so on. We will focus our guidance here on the parts of the project written in Go.
Server projects typically won’t have packages for export, since a server is
usually a self-contained binary (or a group of binaries). Therefore, it’s
recommended to keep the Go packages implementing the server’s logic in the
internal
directory. Moreover, since the project is likely to have many other
directories with non-Go files, it’s a good idea to keep all Go commands together
in a cmd
directory:
project-root-directory/
go.mod
internal/
auth/
...
metrics/
...
model/
...
cmd/
api-server/
main.go
metrics-analyzer/
main.go
...
... the project's other directories with non-Go code
In case the server repository grows packages that become useful for sharing with other projects, it’s best to split these off to separate modules.