Go 🐹

Hero image for Go 🐹

The Go language comes with its own sets of recommendations. Nimble general code conventions apply when they do not go against these recommendations.

Linting

We mainly use the built-in command go vet for automated code checks.

Formatting

  • Use a tab of 4 spaces for indentation.
  • Format the syntax using go fmt.
  • Use a single blank line to break between statements and organize logical chunks of code.
  • End each file with a newline.

Naming

  • Use UpperCamelCase for public and lowerCamelCase for private interfaces, structs, variables and functions.

    // Bad
    type hello_world interface{}
    
    type fizz_buzz struct{}
    
    type HtmlEvent struct{}
    
    var my_array []string
    my_array := []string{}
    
    func foo_bar() {
        // some code
    }
    
    func PublishApi() {
        // some code
    }
    
    // Good
    // public
    type HelloWorld interface{}
    
    type FizzBuzz struct{}
    
    type HTMLEvent struct{}
    
    // private
    var myArray []string
    myArray := []string{}
    
    func fooBar() {
        // some code
    }
    
    func publishAPI() {
        // some code
    }
    
  • Use err as a common variable name for error type.

    // Bad
    someErr := returnErrorFunc()
    
    // Good
    err := returnErrorFunc()
    
  • Use lowercase with NO underscore for package names. Prefer conciseness over long names.

    // Bad
    package string_conversion
    package operatingSystem
    package documentation
    package system_log
    package io_utility
    
    // Good
    package strconv
    package os
    package doc
    package syslog
    package ioutil
    
  • Use meaningful variable names for global/local/argument variables and single-letter names for loop variables.

    // Bad
    var r *redis.Pool
    o := getOrders()
    u := findUser(i)
    
    for index, customer := range customers {
        ...
    }
    
    // Good
    var RedisPool *redis.Pool
    orders := getOrders()
    user := findUser(userID)
    
    for i, c := range customers {
        ...
    }
    

Imports

  • Organize packages in groups, separated by a blank line between each group.

    import (
        // Standard library packages
    
        // Internal project packages
    
        // Third-party packages
    )
    
    // Bad
    import (
        "errors"
        "nimblehq/controllers"
        "net/http"
        "github.com/fizz/buzz"
        "nimblehq/models"
        "github.com/foo/bar"
    )
    
    // Good
    import (
        "errors"
        "net/http"
    
        "nimblehq/controllers"
        "nimblehq/models"
    
        "github.com/fizz/buzz"
        "github.com/foo/bar"
    )
    
  • Sort packages by alphabetical order for each group.

    // Bad
    import (
        "net/http"
        "errors"
    
        "nimblehq/models"
        "nimblehq/controllers"
    )
    
    // Good
    import (
        "errors"
        "net/http"
    
        "nimblehq/controllers"
        "nimblehq/models"
    )
    
  • Use lowercase with NO underscore to rename imported packages. This is often needed to avoid package import conflicts.

    // Bad
    import (
        "errors"
        goUrl "net/url"
    
        errors_config "mypackage/config/errors"
        "mypackage/config/url"
    )
    
    // Good
    import (
        "errors"
        gourl "net/url"
    
        errorsconf "mypackage/config/errors"
        "mypackage/config/url"
    )
    
  • Prefer naming modules in go.mod using the repository-hosting-domain/user-name/repository-name pattern.

    // Bad
    module my-project
    
    // Good
    module github.com/nimblehq/my-project
    
  • Use this module name pattern convention when importing internal modules.

    // Bad
    import (
        "my-project/controllers"
        "my-project/models"
    
        "github.com/fizz/buzz"
        "github.com/foo/bar"
    )
    
    // Good
    import (
        "github.com/nimblehq/my-project/controllers"
        "github.com/nimblehq/my-project/models"
    
        "github.com/fizz/buzz"
        "github.com/foo/bar"
    )
    

Declaration

  • Prefer grouping all Constants into a single block.

    // Bad
    const failedStatus = "failed"
    const pendingStatus = "pending"
    const processedStatus = "processed"
    
    // Good
    const (
        failedStatus = "failed"
        pendingStatus = "pending"
        processedStatus = "processed"
    )
    

Slices

  • Prefer var s []string over s := []string{} when declaring an empty slice.

Functions

  • Prefer declaring the type for each parameter over declaring a single type for all parameters.

    // Bad
    func fooBar(num1, num2 int, str1, str2 string) {
        // Some code
    }
    
    // Good
    func fooBar(num1 int, num2 int, str1 string, str2 string) {
        // Some code
    }
    
  • Prefer unnamed return parameters.

    // Bad
    func fruit(name string) (color, taste string) {
        switch name {
        case "Banana", "Mango":
            color, taste = "Yellow", "Sweet"
        default:
            color, taste = "Unknown", "Unknown"
        }
        return
    }
    
    // Good
    func fruit(name string) (string, string) {
        var color string
        var taste string
    
        switch name {
        case "Banana", "Mango":
            color, taste = "Yellow", "Sweet"
        default:
            color, taste = "Unknown", "Unknown"
        }
        return color, taste
    }
    
  • Prefer early returns to avoid deep nesting.

    // Bad
    func validUser(u *User) bool {
        if u != nil {
            if u.Id != nil {
                return true
            } else {
                return false
            }
        } else {
            return false
        }
    }
    
    // Good
    func validUser(u *User) bool {
        if u == nil {
            return false
        }
    
        return u.Id != nil
    }
    
  • Prefer one or two letters abbreviation as the function receiver name. Such names should be consistent with the other receiver functions.

    // Bad
    func (registration *Registration) New() {
        ...
        ...
    }
    
    func (serverHandler ServerHandler) ServeHTTP(rw ResponseWriter, r *Request) {
        ...
        ...
    }
    
    // Good
    func (r *Registration) New() {
        ...
        ...
    }
    
    func (sh ServerHandler) ServeHTTP(rw ResponseWriter, r *Request) {
        ...
        ...
    }
    
    • Prefer placing all private functions after the public functions.
    // Bad
    func myPrivateFunction() {}
    
    func MyPublicFunction() {}
    
    // Good
    func MyPublicFunction() {}
    
    func myPrivateFunction() {}
    

Errors

  • DO NOT ignore errors using _ variables if the function returns an error. Check them to make sure the function is completed.

  • Prefer declare then check over inline error handling for error returning functions.

    // Bad
    if err := fooBar(); err != nil {
        // Error handling
    }
    
    // Good
    err := fooBar()
    if err != nil {
        // Error handling
    }
    
  • Prefer reusing the variable err instead of assigning errors to a new variable — errors must be handled right away.

    // Bad
    func1Err := returnErrFunc1()
    if func1Err != nil {
        // Error handling
    }
    
    func2Err := returnErrFunc2()
    if func2Err != nil {
        // Error handing
    }
    
    // Good
    err := returnErrFunc1()
    if err != nil {
        // Error handing
    }
    
    err = returnErrFunc2()
    if err != nil {
        // Error handing
    }
    
  • DO NOT use panic for handling normal errors. Use error and multiple return values.

Testing

  • Any test file must have a _test.go suffix as the go test command automatically executes any file with names that match the file pattern *_test.go.

  • Prefer adding the _test suffix to the package name for any test package instead of using the same name as the original package. This naming keeps in sync the file name with the package name.

    // controllers/foo.go
    package controllers
    
    // Bad
    // controllers/foo_test.go
    package controllers
    
    // Good
    // controllers/foo_test.go
    package controllers_test
    
  • Any test function must have a Test prefix. The go test command automatically executes any function that matches this pattern.

    func TestXxx(*testing.T) {
        // Testing code
    }
    
  • Prefer describing method name with the # prefix and function name with the . prefix in unit tests. This convention is only applicable when using the Ginkgo testing framework.

    // Go method
    func (u *User) Save() error {...}
    
    // Go function
    func IsBlank(value string) bool {...}
    
    // Bad
    Describe("Save", func() { ... })
    Describe(".Save", func() { ... })
    
    Describe("IsBlank", func() { ... })
    Describe("#IsBlank", func() { ... })
    
    // Good
    Describe("#Save", func() { ... })
    
    Describe(".IsBlank", func() { ... })