Follow Go community standards:
// The right way
import "github.com/brnstz/utils"
// The wrong way
import "utils"
// 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"
)
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 [email protected]:brnstz/web.git $MYGO/web
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
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.
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
}
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.
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
}
// 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
}
}
}
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...
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
// Good
h := map[string]bool{}
// Bad
h := make(map[string]bool)