Code style is a set of rules or guidelines when writing source codes of a software project. Following particular code style will definitely help contributors to read and understand source codes very well. In addition, it will help to avoid introducing errors as well.
Project PouchContainer is written in Golang. And currently we use three tools to help conform code styles in this project. These three tools are:
And all these tools are used in Makefile.
When collaborating in PouchContainer project, we follow the style from Go Code Review Comments. Before contributing, we treat this as a must-read.
For a project, existing tools and rules may not be sufficient. To align more in styles, we recommend contributors taking a thorough look at the following additional style rules:
When constructing a struct, if comments needed for fields in struct, keep a blank line between fields. The encouraged way is as following:
// correct example
// ContainerManager is the default implement of interface ContainerMgr.
type ContainerManager struct {
// Store stores containers in Backend store.
// Element operated in store must has a type of *ContainerMeta.
// By default, Store will use local filesystem with json format to store containers.
Store *meta.Store
// Client is used to interact with containerd.
Client ctrd.APIClient
// NameToID stores relations between container's name and ID.
// It is used to get container ID via container name.
NameToID *collect.SafeMap
......
}
Rather than:
// wrong example
// ContainerManager is the default implement of interface ContainerMgr.
type ContainerManager struct {
// Store stores containers in Backend store.
// Element operated in store must has a type of *ContainerMeta.
// By default, Store will use local filesystem with json format to store containers.
Store *meta.Store
// Client is used to interact with containerd.
Client ctrd.APIClient
// NameToID stores relations between container's name and ID.
// It is used to get container ID via container name.
NameToID *collect.SafeMap
......
}
When defining interface functions, we should always explicitly add formal parameters, and this helps a lot to code readability. For example, the following way are preferred:
// correct example
// ContainerMgr is an interface to define all operations against container.
type ContainerMgr interface {
// Start a container.
Start(ctx context.Context, id, detachKeys string) error
// Stop a container.
Stop(ctx context.Context, name string, timeout int64) error
......
}
However, missing formal parameter's name would make interface unreadable, since we would never know what the parameter's real meaning unless turning to one implementation of this interface:
// wrong example
type ContainerMgr interface {
// Start a container.
Start(context.Context, string, string) error
// Stop a container.
Stop(context.Context, string, int64) error
......
}
In addition, a blank line between function's comments is encouraged to make interface more readable.
When importing packages, to improve readabilities, we should import package by sequence:
- Golang's built-in system packages;
- project's own packages;
- third-party packages.
And we should keep a blank line among these three kinds of packages like the following:
import (
"fmt"
"strconv"
"strings"
"github.com/alibaba/pouch/apis/types"
"github.com/alibaba/pouch/pkg/errtypes"
"github.com/alibaba/pouch/pkg/meta"
"github.com/alibaba/pouch/pkg/randomid"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
)
Variable object should be declared at the beginning of the go file following package name and importing.
When generating error in one function execution failure, we should generally use the following way to append string "failed to do something" and the specific err instance to construct a new error:
fmt.Errorf("failed to do something: %v", err)
When an err could be thrown out, please remember to add it in the error construction.
PouchContainer encourages contributors to take advantages of return fast
to simply source code and indent less. For example, the following codes are discouraged:
// wrong example
if retry {
if t, err := calculateSleepTime(d); err == nil {
time.Sleep(t)
times++
return retryLoad()
}
return fmt.Errorf("failed to calculate timeout: %v", err)
}
return nil
In code above, there are some indents which can be avoided. The encouraged way is like the following:
// correct example
if !retry {
return nil
}
t, err := calculateSleepTime(d);
if err != nil {
return fmt.Errorf("failed to calculate timeout: %v", err)
}
time.Sleep(t)
times++
return retryLoad()
No matter log or error, first letter of the message must be lower-case. So, logrus.Debugf("failed to add list: %v", err)
is encouraged. And logrus.Debugf("Failed to add list: %v", err)
is not perferred.
When occurring nesting errors, we recommend first considering using package github.com/pkg/errors
.
Every comment must begin with //
plus a whitespace no matter for a variable, struct, function, code block and anything else. Please don't forget the whitespace, and end up all the sentence with a .
. In addition, it is encouraged to use third person singular to polish the majority of function's comments. For example, the following way
// wrong example
// ExecContainer execute a process in container.
func (c *Client) ExecContainer(ctx context.Context, process *Process) error {
......
}
could be polished to be executes
rather than execute
:
// correct example
// ExecContainer executes a process in container.
func (c *Client) ExecContainer(ctx context.Context, process *Process) error {
......
}
We should take DRY(Don't Repeat Yourself)
into consideration when adding anything.
If you think much more practical code styles should be introduced in PouchContainer. Please submit a pull request to make this better.