kilabit.info
Simple | Small | Stable | Secure

Go (Informal) Coding Style

In general Go already have gofmt that will format the code according to Go standard. Developers should already used this tool in their editor/IDE. This section describe informal coding style, that is not covered by Go format tool.

The following recommendation is subjective. If you work in large code base, with more than three developers, you should already have a common "language" between them, to make it consistent and readable.

Group imports

Imported packages should be grouped and ordered by system, third party, and then our packages. Each group separated by empty line. For example,

import (
	// This is system packages
	"os"
	"net/http"
	...

	// Empty line followed by third party packages
	"third/party/library"
	...

	// Empty line followed by our packages
	"github.com/yourcompany/yourlib"
	...
)

Structure code as in Godoc layout

If you looks at the Godoc layout, each sections is ordered by the following format,

  • package description

  • package constanta

  • package global variables

  • package global functions

  • type

  • type's methods

Builtin functions, like init(), main(), and TestMain() must be at the bottom of the source code. As an example see net package [1].

Rationale: Following godoc format will make code easy to read, because we know where each of section is located.

Package must have a file with the same name

Package mypkg must have source file with the name mypkg.go. This file will be used to declare global variables, constanta, and init() function.

Rationale: easy to search where global variables, constants, and init() defined.

One type (struct/interface) per file

The filename must follow the name of the type. For example, package X have two exported structs: Y and Z. So, in the directory X there would be two files: Y.go and Z.go.

Rationale:

  • Easy to search where type is defined

  • Modularization by files

Define field name when creating struct instance

Bad practice:

x := ADT{
	"a",
	"b",
	"c",
}

Good practice:

x := ADT{
	name: "a",
	phone: "b",
	address: "c",
}

Rationale:

  • Prevent miss-assigned field value when refactoring struct. For example, new field "firstname" and "lastname" added the top of declaration, the "Bad" example still work but may not what developer wants.

  • Easy to read.

Define variable name in function return

This will minimize the chance of duplicate variables (especially error), one time declaration, and allow calling return without parameter.

For example, the following code is ok,

func DoSomething(x, y int) (int, error) {
	...
	err := callOtherFunc(x,y)
	...
	v, err = doOther()
	...

	return v, err
}

But recommended way is,

func DoSomething(x, y int) (v int, err error) {
	...
	err = callOtherFunc(x,y)
	...
	v, err = doOther()
	...

	return
}

Rationale:

  • Programmer can know the type of variable (allow quick reading of code without guessing/searching the type through API)

  • Minimize variable creation. Technically, each := statement will create new variable and the previous one will be garbage collected.

Use short variable names if possible

Common short variable names,

  • x, y, and z for looping. Not i, j, etc. because its prone to typo, and let more than three deeps looping (which is a signal for bad programming) and its not easy for quick reading.

  • err for error variable

  • ctx for context

  • req for client request

  • res for server response

  • msg for general message input/output

Common prefix for variable or function,

  • jXXX for message in JSON struct

  • bXXX for message in slice of bytes ([]byte)

  • DefXXX or defXXX for default variable/constanta

Comment grammar

In Go, exported field or function denoted by capital letter on the first letter, and it must have comment.

For field (on struct, var, or const) the recommended comment format is by using "define" verb after variable name.

For example,

// DefPort define the default port to listen on ...
var DefPort = 9002

For function the recommended format is by using "will" verb after function name if the function or method return an error; and explain what the function does and what the function will return on success and on failure.

For example,

//
// GetEnv will read system environment name `envName` and ...
//
// On success, it will return ...
// On fail, it will return ...
//
func GetEnv(envName string) (v string, err error) {
	...
}

Package that create binary should be in "cmd" directory

One of the things that I learned later in software development was when writing code, pretend that your code will be used by other developers, which means, write library first, program later. This is a mistake that we have been taught since college, because we learn to write program not library.

Go, in their subtle way, embrace this kind of thinking when developing software.