代码规范
代码规范是在编写软件项目源代码时遵循的一套规则或准则。遵循特定的代码规范无疑有助于贡献者更好地阅读和理解源代码,同时也有助于避免引入错误。
代码规范工具
ChaosBlade 项目使用 Golang 编写。目前我们使用以下工具来确保项目中的代码风格一致:
这些工具均已集成在 Makefile 中使用。
代码审查规范
在 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 - 生成操作失败的错误信息
在函数执行失败时生成错误信息,通常应使用以下方式,将 "failed to do something" 字符串与具体的 err 实例拼接构造新的 error:
fmt.Errorf("failed to do something: %v", err)
当可能抛出 err 时,请记得在错误构造中包含它。
RULE006 - 快速返回以减少缩进
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.Debugf("failed to add list: %v", err) 是推荐的写法,而 log.Debugf("Failed to add list: %v", err) 是不推荐的。
RULE008 - 嵌套错误处理
当出现嵌套错误时,我们建议优先考虑使用 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,请提交 Pull Request 来完善本文档。