(显然比简化好得多。)
我相信我们普遍接受这样一个事实:我们应该编写简单易读的代码,这样才能更难产生错误并引发安全问题。我们编写的代码越复杂,就越容易出错、误解或遗漏某些内容。
然而,与此同时,随着时间的推移,随着我们解决边缘情况并添加几十年前首次创建代码时未曾预料到的新功能,函数往往会增长并变得越来越复杂。
复杂
圈复杂度是用来表示程序复杂度的指标。您可以点击链接查看所有详细信息,但归根结底,圈复杂度越高,函数就越复杂。一个函数包含的语句和代码路径越多。
有一个名为pmccabe的优秀旧命令行工具,它能够扫描 C 代码并输出所有函数及其相应复杂性分数的摘要。
在自己的 C 代码中调用此工具是获取可能需要重构的函数列表的理想方法。虽然复杂函数的定义以及分数的具体计算方法并非完全客观,但我相信这种方法已经足够有效了。
卷曲
去年,我在 curl 仪表盘上创建了一个图表,展示了 curl 中性能最差函数的复杂度得分以及 99 百分位的得分。后来,我又添加了 90 百分位的图表。

这张图展示了 curl 中最糟糕的复杂度是如何随时间变化的——就像往常一样,当我们看到一张图或者测量某个东西时,我们突然想要对这张图的糟糕表现做点什么。因为它看起来很糟糕。
最糟糕的
我拿起手术刀,重构了几个我们最复杂的函数,基本上把那些在 curl 中表现最差的函数的复杂度分数减半了。图表右侧的急剧下降让人感觉很舒服。
我当时离开那里一段时间,很高兴至少改善了那里的状况。
几个月后,我又回到了这个话题。我觉得我们可以做得更多,因为最糟糕的情况仍然很糟糕。我们应该设定一个目标,彻底消灭(实际上是改进)curl代码中所有得分高于N的函数。
一个目标
我在给团队的邮件中建议将可接受的复杂度限制设定为 100,这个数字并不算太激进。我发送邮件时,有 7 个函数的得分超过了 100,其中得分最高的函数甚至达到了 196 分。毕竟,就在几个月前,最差的函数得分还超过了 350 分。
如果可行的话,也许我们可以从 100 开始作为最大值,然后逐渐降低它?
为了进一步直观地了解 curl 代码的复杂性(以及理想情况下我们如何改善这种情况),我还为仪表板创建了另外两个图表。
图复杂度分布
第一个函数会获取每一行源代码的函数复杂度得分,然后显示该复杂度得分占源代码总得分的比例。理想情况下,几乎所有代码的得分都应该很低。

这张图展示了100%的源代码,与任何给定时间点的代码大小无关,因为我认为这才是关键所在:任何特定时间点的复杂度分布与代码大小无关。在这张图显示的时间段内,代码大小几乎呈线性增长,因此2010年50%的代码量当然比现在的50%要少得多。
这张图显示了我们曾经经历过相当多复杂度超过 200 的代码,而今天我们终于彻底清除了所有复杂度超过 100 的代码。图中可能看得不太清楚,但黄色区域自 2025 年 5 月 28 日起一直向上延伸。
图平均复杂度
第二张图会获取每行源代码相同的复杂度得分,然后计算该时间点所有代码行的平均复杂度得分。理想情况下,该行代码的平均复杂度得分应该会随着时间的推移而缩短。
经过我们最新的努力,现在显示到2025年中期,复杂度将急剧下降。自2022年以来,平均复杂度已下降了一半以上。

分析师也喜欢它
静态代码分析器在处理更小、更简单的函数时,也能获得更好的结果,并减少误报。它也有助于生成更好的代码。
重构可能会改变现状
当然,将一个复杂的函数重构为几个更小更简单的函数,既可能简单直接,也可能相当复杂。以简化为名进行重构可能很难。这本身就是一个矛盾的说法,而且它当然可能会颠覆现状,甚至可能带来更多错误而不是修复它们。
当然,这样做需要小心,并且需要围绕功能进行可靠的测试套件,以验证大多数功能仍然存在并且具有与重构之前相同的行为。
函数长度
由于相关性很强,最复杂的函数也往往最长。因此,我还制作了一张图表,分别列出了 curl 源代码中最差的函数长度和第 99 个百分位的函数长度。

(该图表有些问题,因为 P99 不可能高于最差值,但图表似乎表明 2024 年底就已经达到最差值了?)
CI 工作让我们保持诚实
为了确保拉取请求中没有任何一个函数意外地将复杂度提升到允许的水平,我们创建了一个脚本,如果任何函数的复杂度检查结果超过 100,CI 作业就会变成红色。这个脚本现在已经到位了。或许以后我们可以降低这个限制?
朝着目标
与其说这是一个目标,不如说这是一个过程。它试图让我们编写更简单的代码,从而帮助我们编写更好、更安全的代码。让我们看看十年后我们会达到什么程度!
截至撰写本文时,以下是目前 curl 中最复杂的函数列表。得分超过 70 的函数如下:
100 lib/vssh/libssh.c:myssh_statemach_act
99 lib/setopt.c:setopt_long
92 lib/urlapi.c:curl_url_get
91 lib/ftplistparser.c:parse_unix
88 lib/http.c:http_header
83 src/tool_operate.c:single_transfer
80 src/config2setopts.c:config2setopts
79 lib/setopt.c:setopt_cptr
79 lib/vtls/openssl.c:cert_stuff
75 src/tool_cb_wrt.c:tool_write_cb
73 lib/socks.c:do_SOCKS5
72 lib/vssh/wolfssh.c:wssh_statemach_act
71 lib/vtls/wolfssl.c:Curl_wssl_ctx_init
71 lib/rtsp.c:rtsp_do
71 lib/socks_sspi.c:Curl_SOCKS5_gssapi_negotiate
这只是目前的情况。我希望情况会继续好转。即使进展可能慢一点,因为我们现在已经解决了所有最糟糕的情况。
一切都是公开的
所有脚本、显示的图表以及背后的数据当然都是公开的。
原文: https://daniel.haxx.se/blog/2025/05/29/decomplexification/