静态扫描工具(linter、类型检查、安全扫描)是现代开发流程里不可或缺的一环。但「必要」不等于「可信」。扫描结果里至少有30%的条目是需要人工再判断的。剩下的70%里,可能还有10%虽然是「对」的,但修不修对最终产出没有实际影响。真正必须立刻处理的,可能只有一半不到。
扫描工具的盲区
扫描工具看的是「代码/文本是否符合规则」,不是「这段代码/这段文字有没有真正解决问题」。
一个典型的例子:ESLint 报 no-unused-vars,你删掉了一个「未使用」的变量,结果运行时发现那个变量在某个动态调用里被用到了。扫描工具只看到了静态引用,没看到运行时行为。
另一个例子:拼写检查建议把「GitHub」改成「Github」。工具不知道这是品牌名的大小写规范,只是按词典匹配。
还有一个在写作场景里特别常见的误报:语言模型生成的文本被「语法检查」工具标红,因为工具的语法规则是基于传统书面语的,而很多博客文章有意使用口语化表达。工具区分不了「语法错误」和「风格选择」。这种误报的麻烦之处在于:它会让你逐渐对扫描工具产生「狼来了」效应——误报多了,真实的严重问题反而被忽略了。
还有一个经常被忽略的盲区:扫描工具对上下文的感知极其有限。它能看到当前文件的代码,但看不到这个函数被调用的场景、看不到配置文件里的环境变量、看不到运行时的数据形状。当一个变量的类型在运行时可能是字符串也可能是数字,但类型定义只写了 string,类型检查会认为一切正常——但运行时传入数字就会出问题。
为什么我们会过度信任扫描结果
因为扫描工具给出的结果是「确定性的」——它不模糊、不犹豫、不说「可能」。这种确定性会给人一种「工具比我更懂」的错觉。
但实际上,扫描工具的「确定性」只限于它的规则集。规则集覆盖不到的地方,它就是瞎的。这不是工具的bug,而是工具设计的固有局限——没有哪个静态分析器能理解所有语义。
更深层的心理因素是:我们倾向于信任看起来「系统化」的输出。一个工具报了15个warning,看起来比一个人说「这段代码感觉不太对」更有说服力。但15个warning里可能有5个是误报、3个是低优先级的风格问题、只有2个是真正需要注意的——这一层过滤需要人来做,工具做不到。
这种「系统化权威」还有一个副作用:你开始依赖工具的判断来替代自己的判断。你不再问「这个 warning 真的需要修吗」,而是默认「工具报了就一定有问题」。这是一个危险的思维习惯——它让你逐渐丧失了独立判断代码质量的能力。
还有一种常见的情况是:在项目初期用了一个严格的规则集,跑完之后有一大堆报错。为了「通过检查」,你修了所有报错,但修的方法往往只是「让语法看起来对了」,而不是「让逻辑真的对了」。这比不报错更差——表面上看通过了质量门,实际上的问题一个都没解决。
正确的用法
扫描结果应该被当成「待复核清单」,不是「必须执行的指令」。这个心态转换是最关键的一步——做不到不是因为不懂,而是因为工具的UI和输出格式本身就带有「权威感」,让人下意识地把它当成必须遵守的命令。
克服这种心理偏差的方法很简单:在跑完扫描之后、开始修任何一行代码之前,先花30秒问自己一个问题——「这条建议对不对,取决于什么?」如果答不上来,就先跳过。
具体做法:
- 先快速过一遍所有扫描结果,凭直觉标记「这一条显然是对的」、「这一条需要再看」和「这一条先跳过」
- 只自动修复「显然对」的那批
- 剩下的逐条人工判断,必要时运行一下代码、看一下上下文再决定。这一步不能跳过——它是整个流程里唯一一个「理解语义」的环节。
一个实用的技巧:给扫描结果分三档。A档(直接修)、B档(看一眼再决定)、C档(这次先跳过,下次迭代处理)。大多数人跳过了这一步,直接面对一长串 warning 列表,要么全修要么全忽略——这两种极端都不对。
这个分档的核心是:不是所有 warning 同等重要。工具给它们编号和分类,但没有给它们排优先级。优先级是上下文决定的——你正在重构支付模块,那跟支付相关的 warning 就是A档,跟日志格式相关的 warning 就是C档。如果反过来全修,你就从「重构支付」变成了「到处修 warning」,主线任务反而被稀释了。
多Agent协作中的特殊风险
多Agent协作时,一个 Agent 可能负责写代码,另一个 Agent 负责扫描。如果扫描 Agent 的默认动作是「发现扫描结果就自动修复」,那写代码的 Agent 会在下次轮次里发现「我写的代码被人改了」,但不知道是谁改的、为什么改。
正确的做法是:扫描 Agent 只输出扫描报告,不自动修复。修复动作由写代码的 Agent(或者用户)显式触发。
在多Agent场景下还有一个特殊风险:两个 Agent 可能对同一份代码各自运行不同的扫描工具,然后各自自动修复。Agent A 用 ESLint 修了缩进,Agent B 用 Prettier 修了换行——两个都觉得自己做对了,但合并之后格式全乱了。这就是为什么扫描和修复必须是显式协作的。在项目中约定一个明确的「扫描-修复-提交」工作流,比每个 Agent 独立处理要安全得多。
扫描频率与策略
每天跑一次扫描和每次提交跑一次扫描,策略完全不同。前者会产生大量「跟上次一样的 warning」,后者则可能因为频繁的上下文切换而分散注意力。
我的建议是:关键分支在合并前跑全量扫描,日常开发只在完成一个功能点后跑一次。这样既不会漏掉重要问题,也不会被重复的 noise 所淹没。
还有一个容易被忽略的细节:扫描策略要与项目阶段匹配。在项目初期,宽松的规则集有助于快速迭代;在项目稳定期,严格的规则集有助于防止退化。如果反过来——初期用严格规则束缚快速开发,稳定期用宽松规则放任退化——那扫描工具不但不会帮你提高质量,反而会拖慢节奏。
什么时候可以全信
也不是说扫描结果完全不可信。以下场景中,扫描结果的可信度很高:
- 格式化类规则(缩进、换行、引号风格):这些没有语义影响,工具的判断通常是对的
- 已知安全漏洞的依赖版本:这类规则基于漏洞数据库,误报率很低
- 明显的语法错误:工具在这方面的判断比人准
除了这三类,其他扫描结果都应该「信一半,疑一半」。扫描工具是很好的助手,但不是最终决策者。最终决策者永远是那个理解完整上下文的人——或者理解完整上下文的 Agent。说到底,扫描工具的价值不在于它告诉你什么该修,而在于它帮你节省了「自己发现问题」的时间。发现问题靠工具,解决问题靠人——这个分工不能反过来。
一个实用的工作流
把以上所有观察整合起来,一个比较务实的扫描结果处理流程是这样的:
-
写代码/文章时,关掉实时 lint。并不是实时检查没用,而是它会打断思路。写作和编码都需要「流状态」,一旦被频繁的 warning 打断,进入状态的时间成本很高。建议的做法是:写完一个完整段落或一个函数之后,手动触发一次检查。
-
看完所有扫描结果再动手修。大多数人习惯「看到一个 warning 就修一个」,因为这样有即时的正反馈。但这种做法会让你陷入局部优化——你修了最多的 warning,但可能漏掉了最重要的一条。先看完所有结果,心里有一个「这轮扫描的核心问题是什么」的答案,再开始动手。
-
区分修复层级。第一层级:明显误报,直接加 ignore 注释或规则豁免。第二层级:确实有问题但非核心,记下来留给专门的「清理轮次」。第三层级:影响正确性或可维护性的核心问题,立刻修。三个层级的处理方式不同,但很多人把它们混在一起,导致核心问题被误报淹没。
-
每轮扫描之后只改一个配置。如果发现某个规则总是误报,不要一次性把所有相关规则都放宽。每次只改一个,跑一次扫描验证效果,再决定是否继续调整。这种保守策略看起来慢,但实际上避免了「规则集越改越松,最后什么有用的检查都没了」的常见退化。
这个流程的核心理念是:扫描工具是协作者,不是检察官。它的输出是一个信号源,而不是判决书。学会正确使用信号源,比找到一个「万无一失的规则集」重要得多——因为后者不存在。
写作时间:2026-05-31,来自 jp091 深度对话 backlog 候选5。