Skip to content

Instantly share code, notes, and snippets.

@brnstz
Last active August 29, 2015 14:12
Show Gist options
  • Save brnstz/d7f7cbefa0b16ae1ddaf to your computer and use it in GitHub Desktop.
Save brnstz/d7f7cbefa0b16ae1ddaf to your computer and use it in GitHub Desktop.
Go coding tips

Go Coding Tips

Go community standards

Follow Go community standards:

$GOPATH setup

Use fully qualified names for imports, even for your own code

// The right way
import "github.com/brnstz/utils"

// The wrong way
import "utils"

Organize/separate imports by core, external and your own code

// The right way
import (
    "fmt"
    "net/http"
    "path"

    "github.com/gorilla/mux"
    "labix.org/v2/mgo"

    "github.com/brnstz/utils"
    "github.com/brnstz/web"
)

// The wrong way
import (
    "fmt"
    "github.com/gorilla/mux"
    "github.com/brnstz/utils"
    "github.com/brnstz/web"
    "labix.org/v2/mgo"
    "net/http"
    "path"
)

Use one $GOPATH

Once your code is imported with full paths, it's easy to use a single $GOPATH. It can be more verbose to get to your dev code, but here's some .bash_profile config that makes this simple:

export GOPATH="$HOME/go"
export MYGO="$GOPATH/src/github.com/brnstz"
alias g="cd $MYGO"

For the initial git clone, run:

git clone git@github.com:brnstz/web.git $MYGO/web

Binaries

Binaries should be packaged so that go install <path to package> builds a package that is correctly named. This can lead to redundant names in the path, but it is preferred over using custom -o options in go build or renaming a binary after the fact. Example:

# Installs a web server in $GOPATH/bin
# The files in /web-server are package main
go install github.com/brnstz/web/web-server

Testing

Organize code for effective testing

Go's test coverage works on a single package at a time. So it's important to trigger test code in its package if you want to get credit for coverage.

Style

Don't be afraid of vertical code

Programmers from the functional tradition can be tempted to write code that executes one or more loops in a single line. Go does not support this functional model and you should not feel bad about it. Many short lines is easier to read than one long line, and it makes the runtime of each code section more obvious.

// This is great! And we know it runs in O(n) time where n=len(people).
names := make(string, len(people))
for i, person := range(people) {
    names[i] = person.Name
}

Don't go struct crazy

Programmers from the Java tradition can be tempted to create an object for each component of functionality, as Java conflates the concept of package and object type. The closest thing to an object in Go is a struct. In Go, you can have any number of structs in a package (even zero). Create structs only to save state associated with methods. To separate functionality, create packages.

Named results / Naked returns OK

In contrast to Go community standards, feel free to use named return parameters and naked returns. It makes functions with multiple return values easier to write, and it's clearer what you are returning in all cases.

But if you do use this style, don't mix in non-naked (clothed?) returns.

// Good
func Foo() (x, y int, err error) {
    x, err = Bar()
    if err != nil {
        // Returns non-nil error
        return
    }

    y, err = Baz()
    if err != nil {
        // Returns non-nil error
        return
    }

    // Returns nil error
    return
}

// Bad, why did you bother with naming the return params?
func Foo() (x, y int, err error) {

    a := 5
    b := 7

    return a, b, nil
}

// Not as bad, but still not great, why not assign to x, y?
func Foo() (x, y int, err error) {
    return 1, 2, nil
}

Prefer early returns over nested if statements

// Good
func Foo() (c int, err error) {

    a, err := Bar()
    if err != nil {
        return
    }

    b, err := Baz()
    if err != nil {
        return
    }

    c = a + b

    return
}

// Bad
func Foo() (c int, err error) {
    a, err := Bar()
    if err == nil {
        b, err := Baz()
        if err == nil {
            c = a + b
            return
        }
    }

}

Limit comment lines to 80 characters

Prose is easier to read when line length is consistent and short. Even if your code spans beyond 80 characters, the comments should not. And speaking of code...

Strive to keep code lines to 80 characters

It won't always be the best solution, but try to keep your code to 80 characters as well. Fewer lines is not less code, it's just less readable.

If this is a challenge, consider some of following:

  • Shorten your variable/function/type names
  • Refactor nested code into functions or early returns
  • Put function arguments on multiple lines

Prefer double brace syntax to make

// Good
h := map[string]bool{}

// Bad
h := make(map[string]bool)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment