Go deadcode:查找没意义的死代码,对于维护项目挺有用!

大家好,我是煎鱼。

还记得我前两年在深圳参加了个技术大会,其中一个议题是携程的一个大佬分享他在日常工作中,发现一大堆过时的无意义代码和逻辑,导致大家工作较为繁琐且较为辛苦的情况。

携程应该是 Java 应用为主,他基于 Java 各种研究,通过 JVM 内参数结合各种手段找到了无意义的死代码,并通过灰度机制等完成了逐步上线和替换。

最近 Go 官方也终于有了类似的工具,今天分享给大家,可以持续关注!

用 deadcode 检测代码

普遍来讲,作为 Go 项目源代码一部分,但在任何执行过程中都无法触及的函数被称为 "死代码",它们会拖累代码库的维护工作。

也会造成程序员在阅读代码时的认知负担,看了半天发现这代码根本没用。或是莫名其妙就被引入模块依赖里里。尴尬得很。

现在我们可以用 deadcode 来识别他。安装方式如下:

shell 复制代码
$ go install golang.org/x/tools/cmd/deadcode@latest
$ deadcode -help
The deadcode command reports unreachable functions in Go programs.

Usage: deadcode [flags] package...

以下是一个简单 Demo:

go 复制代码
func main() {
	var g Greeter
	g = Helloer{}
	g.Greet()
}

type Greeter interface{ Greet() }

type Helloer struct{}
type Goodbyer struct{}

var _ Greeter = Helloer{}
var _ Greeter = Goodbyer{}

func (Helloer) Greet()  { hello() }
func (Goodbyer) Greet() { goodbye() }

func hello()   { fmt.Println("你好,煎鱼!") }
func goodbye() { fmt.Println("再见,煎鱼!") }

运行结果:

go 复制代码
$ go run main.go
你好,煎鱼!

咋一眼一看,可能没法知道是哪块代码没用到。还需要多看两眼。

这时候我们只需要借助 deadcode 工具去扫描,一下子就能得到结果了。

执行如下命令并查看输出结果:

go 复制代码
$ deadcode .
main.go:20:17: unreachable func: Goodbyer.Greet
main.go:23:6: unreachable func: goodbye

检测结果告诉我们 goodbye 函数和 Goodbyer.Greet 方法都无法访问。也就是这个代码本身的存在是没有运行意义的。

如果你希望清除这些骚扰代码,就可以依据这个结果去做删除代码了。

同时也可以借助命令的子选项 -whylive,让检测工具给我们解释为什么 greet.hello 函数是有效的。

如下解释结果:

go 复制代码
$ deadcode -whylive=example.com/greet.hello .
                  example.com/greet.main
dynamic@L0008 --> example.com/greet.Helloer.Greet
 static@L0019 --> example.com/greet.hello
                   example.com/greet.main
  ...

该命令会把 main 开始到函数调用的过程打印出来,作为一种解释。证明这个函数确实是存在使用的。

注意点和发现机制

需要留意的是:deadcode 工具,必须要包含 main 函数。言外之意就是其检测链路是从 main 函数开始的。

否则会产生如下的报错信息:

shell 复制代码
$ deadcode .
deadcode: no main packages

deadcode 工具本身会加载、解析和类型检查指定的包,然后将其转换为类似编译器的中间表示形式。

然后会使用 Rapid Type Analysis(RTA)的算法来建立可达函数集,最初只包括每个主要包的入口点:main 函数和包初始化函数(分配全局变量并调用名为 init 的函数)。

RTA 分析每个可达函数的语句体以收集三种类型的信息:直接调用的函数集合、通过接口方法进行的动态调用集合以及转换为接口的类型集合。

因此他必须依赖 main 函数作为主入口来做调用链路分析。当然了,这也是相对正常的。总得有个 "客户端" 来做开始逻辑。

我们可以定期在 Go 项目上运行 deadcode 命令(特别是在重构工作之后),以帮助识别程序中不再需要的部分。

但需要留意的是,deadcode 工具还是在发展阶段。复杂场景下,可能无法保证 100% 的准确率,我们最好还是要自己做一遍 double check 和灰度上线。

总结

今天基于官方的《Finding unreachable functions with deadcode》给大家分享了 deadcode 工具的使用和机制。

整体上来讲,还是非常乐见这个工具的诞生和发展的。历史项目维护旧了,很多地方删删改改,堆积久了后,确实会给大家开发造成不少的认知负担和维护成本。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读

相关推荐
用户40158233246210 小时前
对话一长,AI 就忘了你说过的话?Claude Code、Codex、Aider、WES Code 怎么压缩上下文
人工智能·go·ai编程
我的div丢了肿么办14 小时前
go语言抽离路由文件以及对路由进行分组
后端·go
孔令飞15 小时前
【OneX】OneX AI 工具安装、配置及功能介绍
云原生·go·ai编程
小满zs17 小时前
Go语言第十四章(文件操作)
后端·go
vipxieliang18 小时前
Go 1.24 工具链改进实战:工具依赖管理与 go vet 新分析器
后端·go
程序员爱钓鱼21 小时前
Go 编程实战:Benchmark——基准测试、testing.B、benchmem、性能对比与代码优化实战
go·排序算法
Thinker QAQ1 天前
并发编程(十一):Shared Memory 与 Message Passing——关系与组合使用
java·python·go·并发编程·shared memory·message passing
喵个咪2 天前
GoWind Kanban 拆解:看板拖拽背后——LexoRank、乐观锁与尽力而为广播
后端·架构·go
喵个咪2 天前
GoWind OA 拆解:用 3000 行 Go 写一个够用的审批工作流引擎
后端·架构·go
喵个咪2 天前
GoWind Ledger 拆解:单式记账的余额一致性——confirm、补偿,以及没进事务的那些缝隙
后端·架构·go