Golang 库开发
· 阅读需 3 分钟
指导原则
- 开发库,写代码反而是简单的部分
- 总有计划外的工作
- 总有新的 feature
- 总有要修的 bug
- 人们衡量开源库的质量基本方式
- 基本的代码质量, go vet, go fmt, go lint
- 测试覆盖率
- 文档
- Issue open / closed 的数量
- GitHub star 数量多并不意味着质量高
- 开源库的四大要素, 可用性/可读性/灵活性/可测性
库的四大要素
可用性: 从使用者角度出发思考问题
比如要写一个 HTTP GET Request ,先创建再发送
import "net/http"
req, err := http.NewRequest(
http.MethodGet, "https://www.google.com", nil /* no body */)
if err != nil {
return err
}
client := &http.Client{}
res, err := client.Do(req)
但是,这种代码会经常被用到,反复写的话会很冗长,那么为什么不直接提供 GET 的接口呢?
import "net/http"
client := &http.Client{}
res, err := client.Get("https://www.google.com")
可读性
灵活性
还是 GET 的例子,如果要加 request logging
import "net/http"
client := &http.Client{}
res, err := client.Get("https://www.google.com")
有一个可替换的 RoundTripper interface
type Client struct {
// Transport specifies the mechanism by which individual
// HTTP requests are made.
// If nil, DefaultTransport is used.
Transport RoundTripper
type RoundTripper interface {
RoundTrip(*Request) (*Response, error)
}
这样只需要指定一个 loggingRoundTripper
就可以了,不需要 wrap
import "net/http"
client := &http.Client{Transport: loggingRoundTripper}
res, err := client.Get("https://www.google.com")
可测性
- 不仅要自己保证自己可测
- 还提供 test helper 给 caller 用
向后兼容
- 任何 exported entity
- 重命名、移除
- 函数参数类型的修改
- interface 加一个 method
这些都有可能导致 breaking change,这个时候就需要 semantic version 了
版本控制
Semantic Versioning 2.0.0 | Semantic Versioning
MAJOR.MINOR.PATCH
- patch: a bug fix
- minor: a new non-breaking change
- major: a breaking change
Stable / Unstable?
v < 1.0
unstable- 从 0.1.0 开始,每个 release 的时候 increment MINOR,bug fix 的时候 increment PATCH
v >= 1.0
stable 正式版 release 1.0
Version Bump 很难,在 micro services 大系统中的核心库大概会花 6 个月到 1 年来更新所有的 dependencies
^1.1 is short for >= 1.1, < 2;
~0.2 is short for >= 0.2, < 0.3
Pin to version range vs Lock exact version