Craig Weber

Getting started with Go, 2018 edition

A little over 2.5 years ago, I wrote a tutorial about installing Go. Since then, one of the more significant changes to the Go ecosystem has been the addition of modules, which effectively does away with the hardest part of installing Go–$GOPATH. This change occurred in the latest version: Go 1.11.

In addition to installing Go, I wanted to make a guide that can get you from nothing to a real project in half an hour. Most languages focus their introductory material on the language and briefly cover setting up a toy program. When you’re done, you realize you have no idea how to build a multi-file program, how to add dependencies (or at least how to add them in a way that won’t break other things on your system), how to get an editor up and running, etc.

I’m not going to focus much at all on Go the language here, since it’s super easy to learn and there are already many great tutorials (the official Tour is probably not a bad place to start). I’m only going to go deep enough to give you a lay of the land; if I’ve done my job, it should be easy enough to Google for specific resources on any given topic (for example, testing).

Now without further ado…

Installing Go

Here are the updated installation instructions:

  1. Download Go

  2. Install it

    2.1. If you’re on Linux, untar it and put it somewhere like /usr/local/go.

    2.2. If you’re on OSX, run the installer. This will install go to /usr/local/go.

  3. Add the go binary to your $PATH. This is probably just editing the line in your ~/.bashrc or ~/.bash_profile or etc that looks like this: PATH=$PATH:... by suffixing it with :/usr/local/go/bin (or the ./bin directory inside of the Go installation directory). Now if you run go version, you should get go version go1.11.1 linux/amd64 or comparable.

That’s it. Now you can compile any Go program.

Text Editor

Go has the highest quality text editor plugins of any programming language I’ve used. The plugins all have the features you would expect from an IDE–the ability to get the type information for a symbol or to get its documentation or to navigate to its source code. I’m aware of high-quality plugins for VS Code, vim, emacs, and Sublime, but there are probably others.

Importantly, all text editor plugins should support running gofmt on save (most do this by default). This program formats your code with the same style that is used across virtually all Go programs. No more bickering about style in code reviews.

Given that Go 1.11 landed in the last month or two, many tools are still adding support for Go modules, so you will likely see some bugs in some tools for the next month or so. I recommend VS Code plus the Go plugin–in my opinion, it’s the easiest, most stable way to work with Go, especially if you’re more comfortable in a GUI environment (although this vim user has found himself using VS Code more and more lately).

Hello world

Create a new project directory anywhere on your system, say /tmp/hello. Now copy and paste the following into /tmp/hello/main.go:

package main

import "fmt"

func main() { fmt.Println("Hello, world") }

This imports the fmt package from the standard library and uses it to print Hello, world. Now, in your hello directory, run go run main.go to run the file, or go build to build a ./hello binary that you can run.

Note that the package is called main but lives in a directory called hello, and the result of go build is a binary named hello. In Go, a directory constitutes a package, and all .go files in the package must have the same package declaration at the top. Running go build in a main package directory will spit out an executable with the same name as the directory that it lives in (modulo the .exe extension on Windows).

Note also that there were no project configuration files, so you didn’t need to learn a new set of configuration options or configuration file syntax. You didn’t need to figure out how to point the compiler at your source files or tell the compiler the order in which it needs to process them. go build is sufficient to build most Go programs.

Dependencies

Dependency management is a critical function for writing software, yet most languages’ “getting started” guides don’t guide you to the best practices if they even broach the topic. Figuring out the right dependency management tool and the right way to use it was left as an exercise for the reader, but frankly it’s a really complex exercise, especially since many ecosystems have multiple dependency management systems that don’t always play nicely together and each has their respective tradeoffs.

As previously mentioned, Go recently standardized around modules, and while I was skeptical initially, it seems to have done a pretty good job at solving the problem (maybe I’ll change my mind over time).

In your project directory, run go mod init <module-name> (note that your project directory can contain many package directories or it can be a single-package project in which the project directory and the package directory are the same). This will create a go.mod file which contains information about your project’s dependencies. For the most part, the go tool will manage this file (and its sibling, go.sum) for you; the file is human-readable and easy to understand, but you should only need to touch it very rarely. To add dependencies, all you need to do is import them in your source code and run go build. The Go tool will pick a version of your dependencies, update your go.mod and go.sum files, download the dependencies to your system, and build the binary. Let’s try it:

  1. In your /tmp/hello directory, run go mod init hello.

  2. Modify your main.go by replacing it with the following:

    package main
    
    
    import (
        "github.com/fatih/color"
    )
    
    
    func main() {
        color.Cyan("Hello, world!")
    }
    
  3. Run go build, and observe it downloading your dependency (and its transitive dependencies):

    go: finding github.com/fatih/color v1.7.0
    go: downloading github.com/fatih/color v1.7.0
    go: finding github.com/mattn/go-isatty v0.0.4
    go: finding github.com/mattn/go-colorable v0.0.9
    go: downloading github.com/mattn/go-isatty v0.0.4
    go: downloading github.com/mattn/go-colorable v0.0.9
    

    You’ll only see this output once since those dependencies get cached. Regardless of whether or not you’ve run go build before, you should see a hello binary, just as before. Running it should produce the same output, but in cyan.

    Note that Go pulls dependencies directly from version control, and it uses Git, Subversion, and Mercurial to do this, so make sure you have those installed on your system.

Publishing packages

Go doesn’t have a package repository; to “publish” packages, you just push your code up to Github or BitBucket or wherever (you can even run your own git/hg/svn server). No need to write CI scripts to publish packages for you.

Testing

Go has unit tests built in, so you don’t need to worry about figuring out what unit test library or test runner to install nor how to run them. Just use the standard library testing package and run your tests with go test. Test files can live wherever you want them to, but its relatively common to put them alongside the source code. Test files are suffixed by _test.go, and are treated specially by the Go toolchain. Within a test file, functions that start with Test and take a single *testing.T argument (no returns) are executed as tests. That argument has methods attached to fail, skip, etc.

Here’s a quick toy example:

package arithmetic

import "testing"

func TestAdd(t *testing.T) {
    if result := Add(1, 2); result != 3 {
        t.Errorf("Wanted 3, got %d", result)
    }
}

Documentation

Go doesn’t require you to learn a special documentation syntax like javadoc or Sphinx; it just pulls documentation from your normal code comments. If you push your repo to Github or Bitbucket or similar, you can see its documentation automatically via (for example) https://godoc.org/github.com/fatih/color. No need to configure a CI job to build or publish documentation packages. There is also a subcommand on the go tool called go doc which takes a symbol identifier (such as fmt.Printf) and returns the documentation associated with that symbol; check out go help doc for more details.

Other tools

Go also has support for the following:

  • Benchmarks (via go test)
  • CPU/Memory profiling (via go tool pprof)
  • Linting (via golint and third party linters)
  • Code coverage (via go tool cover)
  • Debugging (via delve)

For a truly comprehensive list, check out Awesome Go

Contact

For questions, corrections, suggestions, or criticism, hit me up via email or Twitter.

Edit (2018-10-20)

Updated according to some feedback.