Skip to content

Latest commit

 

History

History
204 lines (152 loc) · 4.14 KB

Style.md

File metadata and controls

204 lines (152 loc) · 4.14 KB

Baseplate.go Coding Style Guide

Use tools

Make sure your code passes the following tools:

  • go vet
  • gofmt -s
  • staticcheck (via go install honnef.co/go/tools/cmd/staticcheck@latest)

See our IDE/Editor setup doc for ways to doing that automatically.

Exceptions

Generated code (e.g. code generated by thrift compiler) are exempted from this rule.

Import groups

Group your imports into the following 3 groups, in that order, separated by blank lines:

  1. Standard library packages
  2. Third-party packages (non-stdlib packages without github.com/reddit/baseplate.go prefix)
  3. Packages from the same module (packages with github.com/reddit/baseplate.go prefix)

Example:

import (
	"time"

	"github.com/apache/thrift/lib/go/thrift"

	"github.com/reddit/baseplate.go/log"
)

Rename imports

When an imported package name is different from what is implied by the final element(s) of the import path, rename the import it to make it explicit.

Example:

import (
	jwt "gopkg.in/dgrijalva/jwt-go.v3"
	opentracing "github.com/opentracing/opentracing-go"
)

Exceptions

The implied package name of all of the following is still "foo", and thus they do not need to be renamed:

  • .../foo.v1
  • .../foo/v1
  • .../foo-go
  • .../foo.go

Most import tools already understand these conventions.

One-per-line style

When putting all args to a function signature/call to a single line makes the line too long, Use one-per-line style with closing parenthesis on the next line. Please note that in function signatures one-per-line means one group per line instead of one arg per line.

Example:

func foo (
	arg1, arg2 int,
	arg3 string,
	arg4 func(),
) error {
	// Function body
	return nil
}

foo(
	1,
	2,
	"string",
	func() {
		// Function body
	},
)

When writing slice/map literals in one-per-line style, also make sure to put the closing curly bracket on the next line.

Example:

slice := []int{
	1,
	2,
	3,
}

myMap := map[int]string{
	1: "one",
	2: "two",
}

Exceptions

When using slice literal/args as key-value map, use two-per-line style to make sure that we always have them in pair.

Example:

log.Errorw(
	"Something went wrong!",
	"err", err,
	"endpoint", "myEndpoint",
)

Blank line to highlight change of logical level

Sometimes two consecutive lines could be on the same indentation level but not the same logical level. This usually happens when a long if condition is broke into multiple lines. In such cases, please add a blank line between them to highlight the change of logical level.

Example:

if condition1 && condition2 &&
	condition3 && condition4 {

	// do something, note the blank line before this line.
	foo()
}

Don't abuse multiple return values

Go allows multiple return values from a function and we should in general take advantage that, but the number of return values should be limited to a reasonable number. A rule of thumb is that if you ever have the need to split the return values into one-per-line on the callsite like this:

retval1,
  retval2,
  ...
  retvalN := package.FuncWithNReturnValues(a, lot, of, args, ...)

Then define a struct to return will be much better:

type FuncWithNReturnValuesResult struct {
  Retval1 Retval1Type
  Retval2 Retval2Type
  ...
  RetvalN RetvalNType
}

func FuncWithNReturnValues(...) FuncWithNReturnValuesResult {
  ...
}

Exceptions

In general the error return should not be part of the struct and still returned as a separate and the last value. For example:

type FuncWithNReturnValuesResult struct {
  Retval1 Retval1Type
  Retval2 Retval2Type
  ...
  RetvalN RetvalNType
}

func FuncWithNReturnValuesAndError(...) (FuncWithNReturnValuesResult, error) {
  ...
}

Other resources

For things not covered above, use your best judgement and follow industry best practises. Some recommended resources are: