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
The team mainly uses the built-in command go vet
for automated code checks.
Formatting
-
-
go fmt
. -
-
Naming
-
Use UpperCamelCase for public and lowerCamelCase for private interfaces, structs, variables and functions.
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 }
// 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.someErr := returnErrorFunc()
err := returnErrorFunc()
-
Use
lowercase
with NO underscore for package names. Prefer conciseness over long names.package string_conversion package operatingSystem package documentation package system_log package io_utility
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.
var r *redis.Pool o := getOrders() u := findUser(i) for index, customer := range customers { ... }
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 )
import ( "errors" "nimblehq/controllers" "net/http" "github.com/fizz/buzz" "nimblehq/models" "github.com/foo/bar" )
import ( "errors" "net/http" "nimblehq/controllers" "nimblehq/models" "github.com/fizz/buzz" "github.com/foo/bar" )
-
Sort packages by alphabetical order for each group.
import ( "net/http" "errors" "nimblehq/models" "nimblehq/controllers" )
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.import ( "errors" goUrl "net/url" errors_config "mypackage/config/errors" "mypackage/config/url" )
import ( "errors" gourl "net/url" errorsconf "mypackage/config/errors" "mypackage/config/url" )
-
Prefer naming modules in
go.mod
using therepository-hosting-domain/user-name/repository-name
pattern.module my-project
module github.com/nimblehq/my-project
-
Use this module name pattern convention when importing internal modules.
import ( "my-project/controllers" "my-project/models" "github.com/fizz/buzz" "github.com/foo/bar" )
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.
const failedStatus = "failed" const pendingStatus = "pending" const processedStatus = "processed"
const ( failedStatus = "failed" pendingStatus = "pending" processedStatus = "processed" )
Slices
-
var s []string
overs := []string{}
when declaring an empty slice.
Functions
-
Prefer declaring the type for each parameter over declaring a single type for all parameters.
func fooBar(num1, num2 int, str1, str2 string) { // Some code }
func fooBar(num1 int, num2 int, str1 string, str2 string) { // Some code }
-
Prefer unnamed return parameters.
func fruit(name string) (color, taste string) { switch name { case "Banana", "Mango": color, taste = "Yellow", "Sweet" default: color, taste = "Unknown", "Unknown" } return }
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.
func validUser(u *User) bool { if u != nil { if u.Id != nil { return true } else { return false } } else { return false } }
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.
func (registration *Registration) New() { ... ... } func (serverHandler ServerHandler) ServeHTTP(rw ResponseWriter, r *Request) { ... ... }
func (r *Registration) New() { ... ... } func (sh ServerHandler) ServeHTTP(rw ResponseWriter, r *Request) { ... ... }
- Prefer placing all private functions after the public functions.
func myPrivateFunction() {} func MyPublicFunction() {}
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.
if err := fooBar(); err != nil { // Error handling }
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.func1Err := returnErrFunc1() if func1Err != nil { // Error handling } func2Err := returnErrFunc2() if func2Err != nil { // Error handing }
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 thego 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
// controllers/foo_test.go package controllers
// controllers/foo_test.go package controllers_test
-
Any test function must have a
Test
prefix. Thego 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 {...}
Describe("Save", func() { ... }) Describe(".Save", func() { ... }) Describe("IsBlank", func() { ... }) Describe("#IsBlank", func() { ... })
Describe("#Save", func() { ... }) Describe(".IsBlank", func() { ... })