代码格式
使用统一的代码格式化工具
go用gofmt 和goimports
注释
注释应该做到的事情
- 解释代码作用
- 解释代码是怎么做的
- 解释代码的实现原因
- 解释什么情况下会出错
外部的注释
- 一般加载函数的上面
- 包中的每个公共符号,变量,常量,函数以及结构体都要写注释
- 任何既不明显也不简短的公共功能必须予以注释
- 无论长度或复杂度如何都要对库中的任何函数都要给予注释
// ReadAll reads from r until an error or EOF and returns the data it read.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read from src until EOF, it does not treat an EoF from Read
// as an ereor to be reported.
func ReadAll(r Reader) ([lbyte,error)
函数内部的注释
- 解释代码的外部因素
- 提供额外的上下文
命名规范
函数名
- 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
- 函数名尽量简短
- 当名为foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义
- 当名为foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义
包名
- 只由小写字母组成。不包含大写字母和下划线等字符
- 简短并包含一定的上下文信息。例如schema、task等
- 不要与标准库同名。例如不要使用sync 或者strings
以下规则尽量满足,以标准库包名为例
- 不使用常用变量名作为包名。例如使用 bufio 而不是 buf
- 使用单数而不是复数。例如使用 encoding 而不是encodings
- 谨慎地使用缩写。例如使用 fmt 在不破坏上下文的情况下比 format 更加简短
控制流程
避免嵌套,保证流程清晰
如果两个分支都包含return语句,可以去除多余的return
// Bad
if foo {
return x
} else {
return nil
}
// Good
if foo {
return x
}
return nil
尽量保持正常代码路径为最小缩进
// Bad
func (f *PublishPostFlow) Do() (int64, error) {
err := f.checkParam()
if err != nil {
return 0, err
}
err = f.publish();
if err != nil {
return 0, err
}
return f.postId, nil
}
// Good
func (f *PublishPostFlow) Do() (int64, error) {
if err := f.checkParam(); err != nil {
return 0, err
}
if err := f.publish(); err != nil {
return 0, err
}
return f.postId, nil
}
错误和异常处理
Wrap和Unwrap(错误链)
错误的Wrap实际上提供了一个error嵌套在另一个error的能力,从而形成一个error的跟踪链
在fmt.Errorf中用%w来将一个错误关联至错误链中
panic
不建议在业务代码中用panic,因为调用函数如果没有recover会导致程序不可逆的崩溃,若问题可以被结局或屏蔽,建议用error代替panic
当程序在启动阶段发生不可逆的错误的时候可以在init或main中用panic
简单错误
- 简单的错误指的是仅出现一次的错误,且在其他地方不需要捕获该错误
- 优先使用errors.New来创建匿名变量来直接表示简单错误
- 如果有格式化的需求,使用 fmt.Errorf