跳到主要内容

Golang 库开发

指导原则

  1. 开发库,写代码反而是简单的部分
    1. 总有计划外的工作
    2. 总有新的 feature
    3. 总有要修的 bug
  2. 人们衡量开源库的质量基本方式
    1. 基本的代码质量, go vet, go fmt, go lint
    2. 测试覆盖率
    3. 文档
    4. Issue open / closed 的数量
    5. GitHub star 数量多并不意味着质量高
  3. 开源库的四大要素, 可用性/可读性/灵活性/可测性

库的四大要素

可用性: 从使用者角度出发思考问题

比如要写一个 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