高质量代码要求正确可靠,简洁清晰
- 边界条件是否考虑完备
- 异常情况处理是否稳定
- 逻辑尽可能简单,代码易读易维护
编程原则
简单性
- 消除多余的复杂性,以简单清晰的逻辑编写代码
- 不理解的代码难以维护,无法修复、改进
可读性
编写可维护代码的第一步是确保代码可读性
生产力
团队整体工作效率非常重要
编码规范
代码格式
gofmt:自动格式化工具,将 Go 风格统一为官方风格goimports:在gofmt的基础上自动增删依赖包引用,并将依赖按字母排序分类
推荐使用 gofmt 自动格式化
注释
- 注释应包含声明的每个公共符号:变量、常量、函数、结构等
- 任何不明显也不简短的公共功能必须注释
- 无论长度和复杂度,任何函数都必须注释
例外:不需要注释接口的实现
注释应解释以下几个方面:
- 代码作用:公共符号
- 如何实现
- 实现原因:代码外的因素,额外上下文
- 出错情况:代码的限制条件、处理方法
代码是最好的注释,注释应提供代码未表达的信息
命名规范
变量命名
- 简洁胜于冗长,但要明确功能
- 缩略词全大写,但位于开头且
private时全小写 - 变量距离使用位置较远时尽量多携带一些上下文信息
函数命名
- 函数名不包含包名上下文信息(调用时通常函数与包名是同时出现的)
- 函数名尽量简短
- 当名为
foo的包函数返回Foo类型时,可以省略返回类型,不会引起歧义 - 当名为
foo的包函数返回非Foo类型时,可以在函数名中添加返回类型信息
包名
- 只用小写组成,不包含大写字母、下划线
- 简短并包含一定的上下文信息
- 不与标准库同名,如
sync、strings等 - 尽量不要使用常用变量名作为包名,如
bufio不用buf - 尽量使用单数而不是复数,如
encoding不用encodings - 谨慎使用缩写,要求不影响上下文阅读,如
fmt不用format
流程控制
- 避免嵌套
- 当多个分支都有
return,去除冗余分支
- 当多个分支都有
- 尽量保持正常代码路径为最小缩进
- 优先处理错误情况或特殊情况,尽早结束嵌套
提高可读性和可维护性,故障问题大多出现在复杂条件语句中
错误与异常处理
简单错误
仅出现一次的错误,且在其他位置不需要处理
- 优先使用
errors.New创建匿名变量直接表示简单错误,直接返回或其他处理 - 需要格式化也可以使用
fmt.Errorf
复杂错误
使用 Wrap 与 Unwrap
- 实质是将一个错误嵌套进另一个错误,形成
error链 - 在
fmt.Errorf中使用%w关联一个错误 - 使用
errors.Is判断某错误是否位于错误链中 - 使用
errors.As(err, &subErr) bool在错误链中提取特定错误
panic
若不进行 recover 处理程序将崩溃
- 不建议在程序中使用
panic - 问题可以被屏蔽或解决的情况下应使用
error - 程序启动阶段发生不可逆错误时可以在
main或init中使用panic
recover
在 panic 时触发
- 只能在
defer函数中使用,注意defer先进后出的顺序 - 嵌套无法生效
- 只能在
goroutine中生效 - 可以在其中通过
debug.Stack()记录异常堆栈信息