Skip to main content
版本:Next

开发规范

代码风格

代码风格是编写软件项目源代码时的一组规则或指南。 遵循特定的代码风格肯定会帮助贡献者很好地阅读和理解源代码。 此外,它还有助于减少代码错误。

代码风格检查工具

Chaosblade 项目是用 Golang 编写的。 目前我们使用两个工具来帮助在这个项目中符合代码样式。这两个工具是:

代码审阅评论

在 Chaosblade 项目贡献时,我们遵循 Go Code Review Comments 的风格。 在贡献之前,我们将其视为必读。

额外风格规则

对于一个项目,现有的工具和规则可能还不够。 为了在样式上保持一致,我们建议贡献者彻底查看以下附加样式规则:

RULE001 - 在字段注释之间增加空行

构造结构体时,如果结构体中的字段需要注释,则字段之间留空行。 建议的方式如下:

// 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
......
}

而不是:

// 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
......
}

RULE002 - 在接口定义中标注参数名

在定义接口函数时,我们应该总是显式地添加形式参数,这对代码的可读性有很大帮助。 例如,建议以下方式:

// 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
......
}

但是,缺少形式参数的名称会使接口不可读,除非查看该接口的一种实现,否则我们永远不会知道参数的真正含义:

// wrong example
type ContainerMgr interface {
// Start a container.
Start(context.Context, string, string) error

// Stop a container.
Stop(context.Context, string, int64) error
......
}

此外,函数注释之间的空行会使接口更具可读性。

RULE003 - 引入包

导入包时,为了提高可读性,我们应该按顺序导入包:

  • Golang 的内置系统包;
  • 项目自己的包;
  • 第三方包。

我们应该在这三种包中保留一个空行,如下所示:

import (
"fmt"
"strings"
"time"

"github.com/chaosblade-io/chaosblade/data"
"github.com/chaosblade-io/chaosblade/util"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

RULE004 - 变量声明位置

变量对象应该在包名和导入之后的 go 文件的开头声明。

RULE005 - Generation of action failure

当一个函数执行失败产生异常时,我们一般使用下面的方式附加字符串“failed to do something”和具体的 err 实例来构造一个新的错误:

fmt.Errorf("failed to do something: %v", err)

当可能抛出错误时,请记住将其添加到错误构造中。

RULE006 - 及时 Return 以减少锁进

Chaosblade 鼓励贡献者利用“快速返回”来简化源代码并减少缩进。 例如,不鼓励使用以下代码:

// 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

在上面的代码中,有一些缩进是可以避免的。 鼓励的方式如下:

// 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()

RULE007 - 小写的 log 和 error

无论是日志还是错误,消息的首字母必须小写。 因此,鼓励使用 log.Debugf("failed to add list: %v", err)。 并且不推荐使用 log.Debugf("Failed to add list: %v", err)

RULE008 - 嵌套 error

当发生嵌套 error 时,我们建议首先考虑使用包 github.com/pkg/errors

RULE009 - 正确的注释

无论对于变量、结构、函数、代码块和其他任何内容,每条注释都必须以 // 加一个空格开头。 请不要忘记空格,并以“.”结束所有句子。 此外,鼓励使用第三人称单数来润色大多数函数的注释。 例如下面的方式

// wrong example
// ExecContainer execute a process in container.
func (c *Client) ExecContainer(ctx context.Context, process *Process) error {
......
}

可以修改为 executes 而不是 execute:

// correct example
// ExecContainer executes a process in container.
func (c *Client) ExecContainer(ctx context.Context, process *Process) error {
......
}

RULE010 - 永远记得 DRY

在添加任何内容时,我们应该考虑到 DRY(Don't Repeat Yourself)

RULE011 - 欢迎你的补充

如果您认为 Chaosblade 应该引入更实用的代码样式。请提交 PR 以使其更好。

参考

Pouch Code Style