Skip to content

Commit

Permalink
_content/doc: add guide for organizing a Go module (project layout)
Browse files Browse the repository at this point in the history
Change-Id: I2092a70e0be3dbbd08d0b13c4bd4bd073a504cde
Reviewed-on: https://go-review.googlesource.com/c/website/+/529415
Reviewed-by: Dmitri Shuralyov <[email protected]>
Reviewed-by: Russ Cox <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
  • Loading branch information
eliben committed Sep 19, 2023
1 parent feb9e77 commit df06bf7
Show file tree
Hide file tree
Showing 2 changed files with 293 additions and 0 deletions.
5 changes: 5 additions & 0 deletions _content/doc/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ <h3 id="modules-managing-source"><a href="/doc/modules/managing-source" aria-des
When you're developing modules to publish for others to use, you can help ensure that your modules are easier for other developers to use by following the repository conventions described in this topic.
</p>

<h3 id="modules-layout"><a href="/doc/modules/layout" aria-describedby="index-description">Organizing a Go module</a></h3>
<p>
What is the right way to organize the files and directories in a typical Go project? This topic discusses some common layouts depending on the kind of module you have.
</p>

<h3 id="modules-major-version"><a href="/doc/modules/major-version" aria-describedby="index-description">Developing a major version update</a></h3>
<p>
A major version update can be very disruptive to your module's users because it includes breaking changes and represents a new module. Learn more in this topic.
Expand Down
288 changes: 288 additions & 0 deletions _content/doc/modules/layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
<!--{
"Title": "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](/doc/tutorial/create-module) and
[managing module source](/doc/modules/managing-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
auto_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](https://pkg.go.dev/cmd/go#hdr-Internal_Directories) 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.

0 comments on commit df06bf7

Please sign in to comment.