1. Code
  2. Coding Fundamentals

Let's Go: Golang Code Organization

In this tutorial you'll learn how to organize your Go code in the most effective way that's consistent with Golang's expectations.
Scroll to top

Go is a special language among modern languages. It is very opinionated. For example, there is one true formatting. Go will tell you how to space your code and where to put your curly braces. But it goes much deeper than that. 

Go will also tell you how to capitalize your functions and variables to make them public or private. It will dictate the directory structure of your code. This may come as a surprise for developers coming to Go from more liberal programming languages. 

In this article, I'll explore some of Go's restrictions, discuss their merits, and suggest options for common situations.

Go is an incredibly powerful programming language, learn everything from writing simple utilities to building scalable, flexible web servers in our full course.

Project Euler

When I started learning Go, I created a solution to Problem #6 and just put it in a sub-directory alongside solutions to other problems in other languages. See Project Euler.

The issue is that Go doesn't want you to just scatter Go files randomly all over the place. I later realized that while it works in very simple cases where you don't import other packages, it is not proper.

Dependencies

Every non-trivial program is composed of multiple files or modules or components or classes. I'll just use "file" as a general term. They are often grouped in libraries or packages or assemblies. I'll just use "package" as a general term. The code you write depends on code in other files and packages. 

You need to tell your code how to find those packages and files in order to use their functionality. Each language has its own term: import, include, require. I'll just use "import" as a general term.

Some languages (or language specific tools) also allow you to install dependencies from a remote package repository and install them into a standard local location you can import from.

In most common programming languages, you have a lot of freedom. In C/C++, you tell the compiler/linker where the files and static libraries are (using command-line switches or environment variables like INCLUDE_DIR). In Python, you can install packages from PyPI using setup.py or with pip from PyPI and remote source control repositories. You then import based on the sys.path package search path.

The Go Way

Go, as always, is more prescriptive. It may offend your creativity that you don't get as much say about where to place things, but at the end of the day it doesn't really matter, and there is enough flexibility to accommodate various situations.

Go requires that you put your code in a workspace. A workspace is just a directory with three sub-directories: src, pkg, and bin. It is recommended that you keep all your projects under a single workspace. This way they can depend on each other and share common third-party packages.

Note: I currently work on Windows and use PowerShell for many of the interactive examples. For the following section, I wanted to show the directory structure of my workspace using the tree command. Windows has its own tree.exe command, but it is very limited (no levels). There is allegedly a full-fledged tree command for Windows here

But the site was unreachable. I ended up firing a Docker container running Ubuntu, mounting my Go workspace as /docs/Go, and using the Linux tree command to show it. So don't be confused if you see a Linux environment showing Windows directories and files with .exe suffixes.

Here is my current Go workspace. The bin directory contains various Go commands/tools, and the delve debugger. The pkg dir has a sub-directory with the platform (Win 64) that contains the packages organized by their origin (github.com, golang.com, etc.). The src directory has similar sub-directories for the origin repository or website (github.com, golang.org, etc.).

1
root@67bd4824f9d5:/docs/Go# tree -n -L 3
2
3
|-- bin
4
|   |-- dlv.exe
5
|   |-- go-outline.exe
6
|   |-- go-symbols.exe
7
|   |-- gocode.exe
8
|   |-- godef.exe
9
|   |-- golint.exe
10
|   |-- gometalinter.exe
11
|   |-- gopkgs.exe
12
|   |-- gorename.exe
13
|   |-- goreturns.exe
14
|   `-- guru.exe
15
|-- pkg
16
|   `-- windows_amd64
17
|       |-- github.com
18
|       |-- golang.org
19
|       |-- gopkg.in
20
|       `-- sourcegraph.com
21
`-- src
22
    |-- github.com
23
    |   |-- alecthomas
24
    |   |-- derekparker
25
    |   |-- go-web-crawler
26
    |   |-- golang
27
    |   |-- google
28
    |   |-- lukehoban
29
    |   |-- multi-git
30
    |   |-- newhook
31
    |   |-- nsf
32
    |   |-- rogpeppe
33
    |   |-- tpng
34
    |   `-- x
35
    |-- golang.org
36
    |   `-- x
37
    |-- gopkg.in
38
    |   `-- alecthomas
39
    `-- sourcegraph.com
40
        `-- sqs
41
42
43
27 directories, 11 files
44

Let's take a look inside one of the source projects I created under src: the go-web-crawler. It is pretty simple here: just a flat list of Go files, a license, and a README file.

1
root@67bd4824f9d5:/docs/Go# tree src/github.com/go-web-crawler/ -n
2
src/github.com/go-web-crawler/
3
|-- LICENSE
4
|-- README.md
5
|-- channel_crawl.go
6
|-- main.go
7
`-- sync_map_crawl.go
8
9
0 directories, 5 files

GOROOT and GOPATH

Two environment variables control your destiny in the land of Go. GOROOT is where the Go installation is:

1
09:21:26 C:\Users\the_g\Documents\Go> ls Env:\GOROOT

2


3
Name                           Value

4
----                           -----

5
GOROOT                         C:\Go\

6


7
09:21:35 C:\Users\the_g\Documents\Go> ls c:\go

8


9


10
    Directory: C:\go

11


12


13
Mode                LastWriteTime         Length Name

14
----                -------------         ------ ----

15
d-----        8/16/2016  10:38 AM                api

16
d-----        8/16/2016  10:38 AM                bin

17
d-----        8/16/2016  10:38 AM                blog

18
d-----        8/16/2016  10:38 AM                doc

19
d-----        8/16/2016  10:38 AM                lib

20
d-----        8/16/2016  10:38 AM                misc

21
d-----        8/16/2016  10:38 AM                pkg

22
d-----        8/16/2016  10:38 AM                src

23
d-----        8/16/2016  10:38 AM                test

24
-a----        8/16/2016   1:48 PM          29041 AUTHORS

25
-a----        8/16/2016   1:48 PM           1168 CONTRIBUT

26
-a----        8/16/2016   1:48 PM          40192 CONTRIBUT

27
-a----        8/16/2016   1:48 PM           1150 favicon.i

28
-a----        8/16/2016   1:48 PM           1479 LICENSE

29
-a----        8/16/2016   1:48 PM           1303 PATENTS

30
-a----        8/16/2016   1:48 PM           1638 README.md

31
-a----        8/16/2016   1:48 PM             26 robots.tx

32
-a----        8/16/2016   1:48 PM              5 VERSION

Note that the Go root directory looks like a superset of a workspace with the src, bin, and pkg directories.

GOPATH points to your workspace. That's how Go finds your code.

1
09:21:53 C:\Users\the_g\Documents\Go> ls Env:\GOPATH
2
3
Name                           Value
4
----                           -----
5
GOPATH                         c:\Users\the_g\Documents\Go

There are a bunch of other Go related environment variables, many of which you were required to set in the past (e.g. GOOS and GOARCH). Now, they are optional, and you should not mess with them unless you really need to (e.g. when cross-compiling). To see all the Go environment variables, type: go env.

1
09:51:10 C:\Users\the_g> go env
2
set GOARCH=amd64
3
set GOBIN=
4
set GOEXE=.exe
5
set GOHOSTARCH=amd64
6
set GOHOSTOS=windows
7
set GOOS=windows
8
set GOPATH=c:\Users\the_g\Documents\Go
9
set GORACE=
10
set GOROOT=C:\Go
11
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
12
set CC=gcc
13
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0
14
set CXX=g++
15
set CGO_ENABLED=1

Installs and Imports

When you create a Go program or a library, you can install it. Programs go to your workspace's bin directory, and libraries go to the workspace's pkg directory. On Windows, I discovered that your %GOPATH%/bin is not in the %PATH% directory, so Windows couldn't find my executable. I added it to the Windows PATH and everything worked. Here is how to check in PowerShell that your PATH contains your workspace bin directory:

1
10:56:19 C:\Users\the_g> $env:path.split(";") | grep go

2


3
C:\Go\bin

4
c:\Users\the_g\Documents\Go\bin

Let's see all that in action.

If I go to my go-web-crawler directory and type go install then go-web-crawler.exe is created in c:\Users\the_g\Documents\Go\bin:

1
11:09:18 C:\Users\the_g> ls $env:GOPATH/bin

2


3


4
    Directory: C:\Users\the_g\Documents\Go\bin

5


6


7
Mode                LastWriteTime         Length Name

8
----                -------------         ------ ----

9
-a----        8/15/2016  11:05 AM       15891456 dlv.exe

10
-a----        8/15/2016  10:08 AM        3972608 go-outline.exe

11
-a----        8/15/2016  10:10 AM        4502528 go-symbols.exe

12
-a----        9/18/2016  10:14 AM        1849856 go-web-crawler.exe

13
-a----        8/15/2016  10:08 AM       12097024 gocode.exe

14
-a----        8/15/2016  10:17 AM        6642688 godef.exe

15
-a----        8/15/2016   9:32 AM        6625792 golint.exe

16
-a----        8/15/2016  10:14 AM        6352896 gometalinter.exe

17
-a----        8/15/2016  10:10 AM        2738688 gopkgs.exe

18
-a----        8/15/2016  10:10 AM        6961152 gorename.exe

19
-a----        8/15/2016  10:09 AM        7291904 goreturns.exe

20
-a----        8/15/2016  10:11 AM        9722368 guru.exe

I can now run it from my Go web crawler from anywhere.

1
11:10:32 C:\Users\the_g> go-web-crawler.exe
2
3
found: http://golang.org/ "The Go Programming Language"
4
found: http://golang.org/cmd/ ""
5
not found: http://golang.org/cmd/
6
found: http://golang.org/pkg/ "Packages"
7
found: http://golang.org/pkg/os/ "Package os"
8
found: http://golang.org/pkg/fmt/ "Package fmt"
9
found: http://golang.org/ "The Go Programming Language"

Multiple Go Environments

That's all fine, but sometimes life is not so simple. You may want to have multiple separate workspaces. What's more, you may want to have multiple installations of Go (e.g. different versions) and multiple workspaces for each one. You can do this by dynamically setting GOPATH for changing the workspace and setting GOROOT for changing the active Go installation.

There are various open-source projects for vendoring, package management, and virtual environments. For some reason, most don't support Windows. I'm not sure why such tools have to be platform-specific. I may write a cross-platform Go environment manager myself one of these days.

Conclusion

Go is all about eliminating incidental complexity. Sometimes it comes off as very strict and prescriptive. But if you get into the mindset of the Go designers, you start to understand that avoiding, forbidding or mandating certain things really makes everything simpler.