Featured image of post 几个我觉得值得放进 Go 项目的开源工具

几个我觉得值得放进 Go 项目的开源工具

整理几个真实项目里常用的 Go 开发工具:Delve、Air、golangci-lint、sqlc、Task、GoReleaser 和 gotestsum。

最近整理了一下几个 Go 项目里的开发工具。不是那种“必装清单”,更多是我在实际项目里会愿意留下来的东西。

有些工具刚开始看起来只是锦上添花,但项目稍微变大之后,它们能少掉不少重复劳动。

我现在选工具有个很朴素的标准:它最好能落到项目里的固定入口,而不是只存在于某个人的本机习惯里。比如 golangci-lint 能进 CI,gotestsum 能产出报告,GoReleaser 能跟 tag 发布绑定,这类工具的收益会比“我本地装了很好用”稳定得多。

Delve:调试 Go 程序还是绕不开它

Go 里当然可以一直靠日志定位问题,但遇到 goroutine、变量状态、条件分支比较绕的时候,还是调试器更直接。

Delve 是 Go 生态里比较常用的调试器,命令也不复杂:

1
2
3
dlv debug
dlv test ./...
dlv attach <pid>

我比较常用的是本地 dlv debug,还有偶尔调测试时用 dlv test。如果服务跑在容器里,远程调试要注意端口、权限和编译参数,不要默认把调试端口暴露到外网。

还有一个实际点:不要把 Delve 当成日志的替代品。它适合看“当时这个变量是什么”“这个分支为什么走到这里”,但线上问题还是要靠日志、指标和 trace 留证据。调试器解决的是复现后的观察问题,不解决线上证据不足。

Air:本地开发热重载

写 HTTP 服务时,反复 go run main.go 其实挺打断节奏。Air 做的事情很简单:监听文件变化,自动重新构建和启动。

一般项目根目录放一个 .air.toml 就够了:

1
2
3
4
5
6
7
8
root = "."
tmp_dir = "tmp"

[build]
cmd = "go build -o ./tmp/app ./cmd/server"
bin = "./tmp/app"
include_ext = ["go", "tpl", "tmpl", "html"]
exclude_dir = ["tmp", "vendor", "node_modules"]

这个工具不复杂,但能让本地开发体验舒服很多。

golangci-lint:把代码风格问题提前拦住

Go 自带 gofmt 已经解决了大部分格式问题,但项目里经常还会有未使用代码、错误处理不完整、复杂度过高、潜在 bug 之类的问题。

golangci-lint 的好处是可以把多个 linter 统一起来跑:

1
golangci-lint run ./...

我不太建议一上来把所有规则都打开。比较现实的做法是先从少量规则开始,比如 govetstaticcheckerrcheck,等团队习惯了再逐步加。否则第一次接入 CI,可能会刷出一大堆历史问题,最后没人愿意修。

sqlc:SQL 还是手写,但类型别手写

如果项目里 SQL 比较多,我会考虑用 sqlc。它的思路不是发明一个 ORM,而是让你继续写 SQL,然后从 SQL 生成类型安全的 Go 代码。

1
2
3
4
-- name: GetUser :one
SELECT id, name, email
FROM users
WHERE id = $1;

生成之后,在 Go 里调用时就有明确的参数和返回结构。这样比手动 Scan 一堆字段更稳,也比在业务代码里拼 SQL 更容易维护。

我个人更喜欢这种方式:复杂 SQL 仍然能看得见,类型检查交给工具。

它不适合所有项目。SQL 经常动态拼接、查询结构高度不稳定时,sqlc 反而会显得束手束脚。但如果核心查询比较稳定,尤其是后台服务、管理端、报表导出这类场景,它比手写一堆 Scan 靠谱。

Task:比 Makefile 更像配置文件

Makefile 很经典,但在跨平台场景下有时会遇到 shell 差异。Task 使用 Taskfile.yml,写起来更接近普通配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
version: '3'

tasks:
  test:
    cmds:
      - go test ./...

  lint:
    cmds:
      - golangci-lint run ./...

  dev:
    cmds:
      - air

如果项目成员里有人用 Windows,有人用 macOS/Linux,Taskfile 的可读性会好一点。当然,如果项目已经有成熟 Makefile,也没必要为了换工具而换。

GoReleaser 和 gotestsum

GoReleaser 适合 CLI 工具或需要跨平台二进制发布的项目。比如同时构建 Linux、macOS、Windows,不同架构,再顺手生成压缩包、checksum、GitHub Release。

1
goreleaser release --clean

go test ./... 的输出在包多的时候不太好看,失败信息容易被淹没。gotestsum 的价值就是把测试结果整理得更适合人读,也方便 CI 消费。

1
2
gotestsum -- ./...
gotestsum --junitfile test.xml -- ./...

如果只能先放几个,我会按这个顺序来:

  1. golangci-lint:最适合进 CI,收益稳定。
  2. Delve:排查复杂问题时很实用。
  3. gotestsum:测试包多之后很明显。
  4. Air:本地服务开发体验提升明显。
  5. sqlc:项目 SQL 多时再接入。
  6. Task / Makefile:选一个就行,关键是命令入口统一。
  7. GoReleaser:有发布二进制需求时再上。

工具不需要一次性堆满。我的经验是,先把“检查、测试、启动、发布”这几件事规范起来,项目后面会轻松不少。

我也不建议把这类文章看成工具排名。真正应该排优先级的是项目痛点:如果 CI 经常因为低级问题失败,先接 linter;如果本地启动麻烦,先接 Air 或 Task;如果发布靠手工压包,GoReleaser 的优先级就比调试器还高。

参考资料

本文采用 CC BY-NC-SA 4.0 协议,转载请注明出处。
记录工程实践和排障过程。
使用 Hugo 构建
主题 StackJimmy 设计