团队新人排查bug
刚入职的Go开发接手一个遗留项目,本地运行报错但找不到依赖版本冲突。用go mod graph查看完整依赖树,发现A模块引用了v1.2.3的库,而B模块锁死了v1.1.0。通过go mod tidy自动清理无用的间接依赖,再执行go build验证编译通过,半小时内定位并解决了原本可能耗费半天的依赖地狱问题。
从 `go build` 报错到 `go mod tidy` 拉回依赖,中间缺一个能快速查参数、看用法的速查表。这个页面把 go mod、go build、go test 等高频命令及其子命令、常用标志列在一起,每条附带简短说明和典型示例。所有内容直接展示在浏览器中,无需联网加载——适合在终端旁边开个标签页,边敲边查,不用切回文档翻半天。
刚入职的Go开发接手一个遗留项目,本地运行报错但找不到依赖版本冲突。用go mod graph查看完整依赖树,发现A模块引用了v1.2.3的库,而B模块锁死了v1.1.0。通过go mod tidy自动清理无用的间接依赖,再执行go build验证编译通过,半小时内定位并解决了原本可能耗费半天的依赖地狱问题。
周五下午集成测试突然挂掉,日志显示某Linux发行版上go build报undefined symbol。在本地用GOOS=linux GOARCH=amd64交叉编译复现问题,确认是CGO调用了平台特定的系统调用。通过go env -w CGO_ENABLED=0禁用CGO后重新编译,生成的纯静态二进制在CI环境正常运行,避免了临时更换基础镜像的连锁改动。
安全扫描发现项目依赖的旧版gRPC有高危漏洞,但直接升级到最新版怕破坏兼容性。先用go list -m -versions google.golang.org/grpc列出所有可用版本,然后逐个用go mod download和go test ./...跑单元测试,发现v1.50.0之后废弃了旧版DialOption接口。最终锁定v1.49.0作为安全升级目标,只改了三行调用代码。
单体应用膨胀到50万行代码,团队决定按业务拆成独立服务。先用go list -f '{{.Imports}}' ./...扫描各模块的依赖关系,发现user模块不该引用order模块的数据库模型。通过go mod vendor把所有外部依赖固定到vendor目录,再逐步用go build -o /dev/null验证每个新模块的编译独立性,三个月后拆分完成,零线上事故。
后端团队提交的PR中,某函数使用了reflect包动态调用方法,前端团队认为影响可维护性。用go tool cover -func覆盖测试报告显示该函数测试覆盖率仅45%,且通过go vet检查发现reflect.Value.Call存在panic风险。最终用go test -bench .对比性能,发现反射版本比接口版本慢3倍,团队达成共识改用策略模式重写。
| 输入 | 输出 | 说明 |
|---|---|---|
| go mod init example.com/myapp | go: creating new go.mod: module example.com/myapp | 常规:初始化模块,验证 go.mod 文件创建及模块路径格式 |
| go build -o /dev/null ./... | (无输出,编译成功) | 常规:编译当前包及所有子包,验证编译链完整性,无错误即静默 |
| go test -v -run TestFoo ./pkg/ | === RUN TestFoo --- PASS: TestFoo (0.00s) PASS ok example.com/myapp/pkg 0.123s | 常规:运行指定测试函数,-v 输出详细日志,验证测试框架和包路径匹配 |
| go mod tidy | (无输出,或输出:go: downloading github.com/foo/bar v1.2.3) | 边界:当 go.mod 已与源码一致时无输出;有缺失依赖时触发下载,验证依赖解析 |
| go build -tags=debug main.go | (无输出,编译成功) | 边界:构建标签(build tags)条件编译,验证仅当 debug 标签启用时才包含对应代码 |
| go test -count=1 ./... | ok example.com/myapp/pkg1 0.045s ok example.com/myapp/pkg2 0.067s FAIL example.com/myapp/pkg3 0.032s --- FAIL: TestFail (0.00s) fail_test.go:10: expected true, got false | 易错:-count=1 禁用测试缓存,避免误用旧结果;输出显示失败包及具体断言行 |
| go build -ldflags='-X main.version=1.0.0' main.go | (无输出,编译成功) | 易错:-ldflags 注入变量,需确保变量名(main.version)与源码中 var version string 完全匹配,否则注入静默失败 |
1.go mod tidy 删除了间接依赖,导致构建失败
go mod tidygo mod tidy -etidy 默认会清理掉所有未被直接或间接引用的包。如果项目中有通过 build tag 或条件编译才引入的依赖,tidy 会误删,-e 可保留这些包。
2.go build 没指定输出文件名,覆盖了同目录下同名文件
go buildgo build -o myapp默认输出为当前目录名(或包名),如果目录下有同名文件会被静默覆盖。显式指定 -o 可避免意外覆盖。
3.go test 只跑当前包,忽略了子包测试
go testgo test ./...go test 不带路径默认只测试当前包。./... 是递归匹配所有子包,否则子包的测试会被遗漏。
4.go get 升级了依赖但没有更新 go.sum
go get -ugo get -u && go mod tidygo get -u 会升级依赖到最新小版本,但不会自动清理 go.sum 中废弃的校验和。tidy 会重新计算并精简 go.sum。
5.go run 直接执行了非 main 包,报错 'package is not a main package'
go run ./internal/utilsgo run ./cmd/myappgo run 只能执行声明 package main 的包。非 main 包是库代码,没有入口函数,必须用 go build 编译为 .a 文件。
6.go vet 忽略了 shadow 变量问题
go vet ./...go vet -shadow ./...go vet 默认不检查变量遮蔽(shadow)。-shadow 标志启用该检查,能发现内层作用域意外重名的变量。
7.go mod vendor 后忘了提交 vendor 目录
go mod vendorgo mod vendor && git add vendor/vendor 模式需要 vendor 目录在版本控制中。如果只执行 vendor 但不提交,其他开发者拉取后仍会从网络下载依赖。
go test 覆盖率 = 被覆盖语句数 ÷ 总可执行语句数 × 100%
被覆盖语句数测试执行中至少运行一次的语句数总可执行语句数被测包中所有可执行语句总数某 Go 包有 500 条可执行语句,运行 go test -cover 后报告覆盖了 380 条:覆盖率 = 380 ÷ 500 × 100% = 76%。该值由 go tool cover 基于编译插桩统计,不含空行、注释和不可达代码。
常见原因是测试函数签名不对。go test 只认 func TestXxx(t *testing.T) 这种格式,参数必须是 *testing.T,函数名必须 Test 开头且首字母大写。如果写成 func testXxx(t *testing.T) 或 func TestXxx(),编译器不会报错,但测试框架会直接跳过。检查一下函数签名,确保符合规范。
不会,这是正常行为。go mod tidy 会清理掉 go.sum 中所有未使用的依赖校验和,包括间接依赖。如果你之前手动加过一些 go.sum 条目但代码里没用对应的包,tidy 就会删掉。只要代码能正常编译通过,删掉就是安全的。如果担心,可以先 commit 再跑 tidy,方便 diff 对比。
检查包声明是否一致。如果文件 A 是 package main,文件 B 是 package utils,那么文件 A 无法直接调用文件 B 里的函数。同一个包下所有文件的 package 行必须相同。另外,确认文件 B 没有被 build 标签(//go:build)排除,或者文件名没有以 _test.go 结尾(除非在测试中调用)。
可以改,但改完后必须同步修改所有源文件里的 import 路径。go.mod 第一行 module example.com/foo 定义了模块根路径,所有内部包引用都基于这个前缀。比如原来 import "example.com/foo/pkg",改成 module example.com/bar 后,import 要相应改成 "example.com/bar/pkg"。推荐用 gofmt 或 sed 批量替换,避免遗漏。
go get 在 Go 1.18 之前既下载包又安装二进制,但从 1.18 起 go get 只用来修改 go.mod 的依赖版本,不再安装可执行文件。go install 负责编译并安装到 $GOPATH/bin(或 $GOBIN)。现在装工具用 go install example.com/cmd@latest,加依赖用 go get example.com/pkg@v1.2.3。
可以。加编译参数:go build -ldflags="-s -w" 会去掉符号表和调试信息,体积能缩小 30%-50%。如果用了 cgo,尝试设置 CGO_ENABLED=0 编译纯静态二进制。另外注意是否意外嵌入了大文件(//go:embed)。最极端的情况是用 upx 压缩,但会影响启动速度。
不一定,但建议认真对待。go vet 报告的是 Go 团队认为的代码隐患,比如参数顺序错误、闭包捕获循环变量、无用的赋值等。其中大部分是逻辑 bug 的前兆,比如 Printf 格式串与参数不匹配会导致运行时 panic。少数如 unreachable code 可能是故意保留的占位,可以加 //nolint 注释排除。
检查 go.mod 是否提交了。go run 会自动下载依赖到缓存,但 go build 编译后的二进制是独立运行的,不会自动去找缓存里的包。如果依赖是本地路径(replace 指令),确保目标路径存在且可访问。另外,确认 GOFLAGS 环境变量没有设置 -mod=mod 以外的值导致 build 行为不一致。
隐私保证所有计算与处理均在你的浏览器本地完成,输入数据不会上传服务器,也不会保存或共享。