
技术债是可以还的。
欠了技术债,至少你知道自己欠了。代码在那里,issue 在那里,lint 报告在那里,SonarQube 的坏味道列表在那里。它可感知、可量化、可排期。本质上是一本明账,你随时可以翻出来看,然后安排时间去还。
认知债不一样。它的可怕之处不在于"还不起",而在于你根本不知道自己欠了。
你让 AI 写了一段代码,它编译通过,测试通过,跑起来一切正常。三个月后这段代码出问题,你打开文件,发现自己讲不清它为什么这么写。那一刻你才意识到,这段代码在写出来的时候,就没有人真正理解过它。它从来就不难,它只是从未被理解。
这不是"代码难懂",这是"理解从未存在"。
技术债是明账,认知债是暗账。明账可以还,暗账你连借条都找不到。
这篇文章就干一件事:把两种债放在同一盏灯下面,让你看清楚它们差在哪。差的那一点,恰好是 AI 编程时代最贵的那一点。
第一章:技术债的账本
先看技术债为什么"可还"。
技术债这个词被滥用得很厉害,但它能成为一个产业概念,就因为它有一个完整的基础设施在支撑。这个基础设施就是它的账本。
先拆开看,技术债的账本到底记了什么。
第一层,代码本身是账本。坏味道、重复代码、死代码、过长的函数,它们就在那里,任何一个人打开代码库都能看到。你可以给"圈复杂度 45"打分、给"重复代码占比 12%“排名,这些数字是客观的,工具算得出来,团队每个人看到的是同一份数据。
第二层,工具链是记账的。lint 在 CI 里跑,SonarQube 在流水线上扫描,依赖扫描工具告诉你哪个库有 CVE。这些工具不休息、不遗忘、不挑人。它们把"技术债"翻译成了机器可读的报告,挂在每个人都能看到的地方。
第三层,团队流程是账本的分录。评审意见、技术债清单、重构排期、迭代承诺,这些是人为的记账动作,把"代码有问题"转化为"我们有计划还”。它可能不完美,但它让技术债第一次有了"责任人"和"到期日"。
这三层合在一起,技术债就成了一种可治理的对象。
管理层可以决策"这季度还三个债",工程师可以反驳"这个债不该现在还",新人可以查历史记录理解"为什么当时这么写"。技术债的账本让它拥有了普通缺陷没有的东西:可谈判性。
你想想一个典型的还款流程长什么样。
代码评审的时候,reviewer 指出来:“这个函数 300 行,拆一下。“这是第一笔记录,写在 review 评论里:有上下文、有理由、有责任人。
过了几天,SonarQube 扫描报告出来了:“这个模块的圈复杂度 45,超过阈值,标红。“这是第二笔记录,挂在 CI 流水线上,谁都能看。
再往后,你们开了个技术债评审会,把这些问题排了优先级,写进迭代计划:“下个迭代还三个债,每个估了工时。“这是第三笔记录,变成了团队的一个承诺。
发现没有?技术债从产生到偿还,每一步都有记录。有记录就能追踪,能追踪就能度量,能度量就能排期,能排期就能还清。

我把这个过程叫技术债的"还款仪式”。它粗糙、繁琐、经常被人吐槽官僚主义。但它的存在本身就是一个巨大的优势:系统对技术债的感知是全自动的。
工具和 issue 会替你把账记好,不需要你记得。技术债的可还性,不依赖任何人的自觉性,它依赖的是账本。
账本这个东西的价值,你在它存在的时候感受不到,直到它消失。
第二章:认知债没有账本
现在看认知债。
六个月的保质期
先讲一个场景。两个工程师,同一家公司,同一个月入职,能力相当。
A 工程师接了一个需求:给系统加一个带状态机的限流器。他花了两天,手写代码,画了状态迁移图,写了注释,给同事讲了讲设计取舍:为什么用固定窗口而不是滑动窗口,边界突刺怎么办。Review 的时候同事问了几个问题,他一一解释,同事点头。
B 工程师接了一模一样的需求。他打开 AI 工具,描述了需求,两分钟后代码出来了。他扫了一眼,编译通过,测试通过,功能符合描述。他把代码提交了,感觉良好。同样的需求,A 花了两天,他只花了两个小时,效率是 A 的八倍。
六个月后,需求变更:限流规则要调整,状态机要加一个状态。
A 打开自己的代码,回忆了五分钟,开始改。他知道每个状态为什么存在,知道迁移规则背后的业务含义,知道改哪里会牵连哪里。两天后改完,加了两组测试,上线顺利。
B 打开代码,开始读。他读懂了每一行代码在做什么,毕竟语法是清楚的,但他不知道为什么这么设计。为什么用固定窗口?为什么 SOFT_LIMIT 下面放行规则和 NORMAL 一样?为什么这个状态从这里迁移到那里而不是直接回 NORMAL?
你从不知道,而不是忘了
这些问题的答案,在六个月前那个"感觉良好"的两分钟里,从来没有存在过。不是被忘记了,是从未被生成。
B 花了整整一周才改完,改了三个 bug,其中一个还是线上才发现的。

同样是还债,A 还的是自己欠的债,他知道债在哪。B 还的是别人(或者说是机器)欠的债,他甚至不知道债长什么样。
我特意用了"别人欠的债"这个说法,因为 B 的处境比"忘事"更糟糕。忘事的前提是"曾经知道”。你忘了一件事,至少说明它曾经在你的认知里存在过。而 B 面对的是从未进入过他认知的东西。
他的问题比记忆问题更深一层,是存在性问题:他从来没有理解过这段代码,无论他怎么努力回忆,都回忆不出不存在的东西。
这是认知债最残忍的地方:你从来不知道,而不是忘了。而人的认知系统对"自己不知道"这件事,在代码正常运行时是毫无感知的。你不可能主动去补一个自己不知道缺什么的东西。心理学管这叫"元无知”:你不知道自己不知道。
这也是为什么"多读书多学习"这种建议,对认知债完全无效。你可以主动学习任何你"知道自己不懂"的东西。但认知债恰恰是你"不知道自己不懂"的部分。你不可能给一个你看不见的坑填土。
必须说清楚一点,免得被误解:认知债并不是"不可检测”。它是"没有自动检测”。B 打开代码读不懂的时候,他其实能感知到"读不懂”,但只有在需要改代码这个场景触发时,他才会去读。代码正常运行的那几个月里,没有任何信号会提醒他"这里有债"。认知系统的默认是节能的,检测不会自己启动,它需要一个外部事件来触发。
这就是认知债和技术的本质差异:技术债的债务人是代码,认知债的债务人是人。 技术债的账本记在工具链里,认知债的账本不存在,因为认知债本身就没有"记录"这个环节。它只存在于人的脑子里,而脑子不会自动记账。
你无法给一个"从未被记录的东西"记账。
第三章:为什么工具链检测不到
到这里,一个更尖锐的问题自然浮现:为什么所有质量工具都在检测代码,却没有一个工具在检测理解?
答案很残酷:因为理解不在代码里,而工具只能检测代码里有的东西。
编译器校验什么?语法、类型、可编译性。它不校验"你为什么这么写"。
Lint 管格式、管命名、管常见反模式,但"这个设计决策有没有人验证过"不在它的职责表上。
测试管行为是否符合预期,“写这段代码的人是否理解它"不在它的检查清单里。
编译器、Lint、测试,都假设了一件事
整个工程基础设施,从编译器到 CI 到代码评审,全部建立在同一个假设上:代码是被理解的。这个假设在 AI 出现之前基本成立。人能写出编译通过的代码,通常意味着他至少理解自己写了什么。即使不理解,他也会因为痛苦(debug 的痛苦)而被迫理解。
AI 出现之后,这个假设第一次失效了。
我做了个实验。我用 AI 生成一个带状态机的 HTTP 限流器,需求写得很清楚:每秒最多 100 个请求,四个状态,迁移规则逐条列出。AI 两分钟生成了 140 行代码,我编译,通过。
然后我逐行检查,问了一个问题:这些设计决策,有没有任何一个在代码里留下"为什么”?
一行代码的顺序错误
结果很具体:
- 固定窗口计数:AI 自己选的方案,代码里没有任何注释说明为什么不用滑动窗口或令牌桶。固定窗口在窗口边界会有两倍突刺——对 A 工程师来说这是取舍(他知道并接受),对 B 的代码来说它是无主的选择(没人知道这是取舍还是疏忽)。
- SOFT_LIMIT 回落规则:需求里根本没写"count 降到 80 以下回 NORMAL",AI 自己补的。这个规则是谁定的?没人知道。
- 放行语义偏差:代码是先
count++再检查状态,窗口内计数实际会记到 101(100 个放行 + 1 个被拒也入账),而阈值用>100判断——需求写的是"每秒最多 100 个",代码的对齐方式是"记 101 次再锁"。这个差 1 的错位,没有已存在的测试能抓到。它通过了你写的全部测试,因为没有人写过针对它的测试——而"没人想到写这个测试"本身,就是认知债的表现。 - 硬限流期间计数继续累加:HARD_LIMIT 状态下 count 仍在递增,恢复后可能瞬间又触发限流。没有注释解释这个行为是有意还是无意。
- SOFT_LIMIT 状态没有行为差异:进入 SOFT_LIMIT 后放行规则和 NORMAL 完全一样。它可能有个合理解读(响应头把状态暴露给调用方,供监控告警用),但代码里没有任何文字确认这是设计意图——这种"可能是这样,也可能不是"的不确定性,正是认知债的本质。

五处设计决策,无一处有理由;一处对齐错位,没有现成的工具会检测它。你注意到没有,B 工程师六个月前遇到的"SOFT_LIMIT 为什么和 NORMAL 行为一样",正是这段代码里的第 5 处意图缺失——他当时感觉良好的两分钟,就是在为今天的一周还债。
我盯着这份代码看了很久,脑海里冒出一句话:“这代码能编译,能过测试,但它在默认的抄写员用法下,从来没有被理解过——它只是被生成过。”
这句话就是这篇文章的题眼。

技术债的检测工具检测的是"代码里有的东西"。但 AI 制造的债务,缺失的部分恰恰不在代码里。它在生成那一刻就没有进入代码。工具链检测不到认知债,因为债务本身就不在工具能检测的维度上——工具不是不够聪明,是检测的维度根本不同。
就像你无法用体重秤测量一个人的负债率。
第四章:AI 是怎么让债滚雪球的
有人会说:认知债又不是 AI 发明的。学生抄答案,工程师复制粘贴 Stack Overflow,一样是跳过理解。
对,这个说法没错。认知债这个概念,是 Margaret-Anne Storey 在 2026 年初正式提出的:AI 生成代码的速度超过团队吸收的速度,导致的共同理解侵蚀。这个概念随后在 Hacker News 上引发了大规模讨论,Simon Willison 专门写了博客推荐,Martin Fowler 也撰文讨论过。它确实不是 AI 的发明。早在 AI 编码工具出现之前,复制粘贴别人的代码、囫囵吞枣地改两行就提交,一直是新手工程师的常态。你甚至可以追溯到更远:查手册翻文档的时候,跳过"为什么"直接抄"怎么做",是每个程序员都干过的事。
我提这个概念,不是想加入术语争论。术语本身不重要,重要的是它指向的那个真问题。Storey 的定义是团队层面的:整个团队对系统的共同理解在消失。而我想讨论的是它在个人层面怎么起作用:一个人、一段代码、一个"从未被理解"的模块。团队层面的债是无数个个人层面的债叠出来的,先从个人看起,看得更清楚。
但 AI 改变了两个变量,把认知债从"偶发事件"变成了"默认状态"。
两个变量:速度与默认化
第一个变量是速度。 抄答案和复制粘贴,都是有成本的。你要找到答案,要判断答案对不对,要把它适配到你的场景。这个过程本身就是一种过滤:完全不懂的人,连"找到对的东西"都做不到。而 AI 把"生成"的成本压到了接近零。B 工程师那"感觉良好的两分钟",在过去根本不存在。过去你必须至少理解一点,才能把代码拼起来。现在你什么都不用理解,两分钟就能得到一份编译通过的代码。
第二个变量是默认化。 过去,跳过理解是一个主动选择,你要"去抄"才抄。现在,跳过理解是默认路径,你不主动要求理解,理解就不会发生。人的认知系统是节能的,默认路径就是最省力的路径。当"让 AI 写然后直接提交"成为默认,认知债的积累就变成了自动驾驶。
平滑性幻觉
还有一个更隐蔽的机制,我把它叫"平滑性幻觉"。技术债会以报错、卡顿、烂代码的形式不断骚扰你,它是痛的,痛让人无法忽视。而认知债是产生时无痛的:代码在跑,测试在绿,一切信号都在说"一切正常"。偿还时却剧痛,那种痛在半年后等你。痛苦是债务的天然报警器,认知债把报警器拆了,只留下一个不断增长的隐藏余额。

我把这个过程叫"滚雪球"。雪球越滚越大固然可怕,更可怕的是它根本停不下来——技术债有账本、有排期、有还款仪式,再大也有个盘子兜着;认知债的雪球,没有任何刹车。你每个月让 AI 写五百行代码,五个月后,在你以抄写员模式使用的前提下,你积累了 2500 行"从未被理解"的代码,而你浑然不觉,你的认知债在增长,但没有一个仪表盘会显示这个数字。
我自己的体验也是这样。我们维护一套自己的写作工程系统,里面有不少代码是 AI 辅助写的。系统跑得很稳,稳定得让人安心。直到有一次要做架构调整,需要动一个核心模块,我们的内容发布脚本,正好是 AI 写的。我打开文件,能读懂每一行,但读不懂"为什么"。
那是一种很奇怪的体验:语法全懂,逻辑全对,但你不敢改。因为你不知道改了这一处,会破坏什么你没有意识到的约定。你面对的是一段从未被理解过的代码,它的难懂和普通难懂代码完全不同。
最后我花了整整两个下午,用最笨的办法:把状态机每一步跑一遍,把所有分支画出来,才敢动手。而这段代码,只有四百行。
回头想,这两个下午是可管理的成本——但前提是我主动去还了。如果我不主动,这笔债会一直躺在那里,直到下一次架构调整,或者下一个接手的人。

这里有一个重要的区分,也是我想为 AI 工具说句公道话的地方:问题不在 AI,在输入模式。
同一个工具,两种用法,产生完全不同的结果。
用法一,AI 是导师:你让它生成代码,然后逐行追问:为什么用这个方案?这个边界条件怎么处理?如果并发量翻十倍会怎样?每一次追问,都是一次理解。这种用法下,AI 的效率和人的理解同步增长,工具越好,人越强。
用法二,AI 是抄写员:你让它生成代码,扫一眼,提交。代码是你"拥有"的,理解是别人(机器)的。这种用法下,效率的每一分增长,都在透支理解。
业界数据也支持这个区分。Stack Overflow 与 OpenAI 合作的开发者调查显示,用 AI 学习的人数占比从 2024 年的 37% 涨到了 2026 年的 64%,几乎翻倍,而且在逐年增长(2025 年是 44%)。也就是说,越来越多的开发者正在主动把 AI 当作学习工具。认知债是"抄写员模式"的必然产物,跟 AI 本身无关。

同样的工具,导师模式让人变强,抄写员模式让人欠债。这就像健身器材:拿来练是增肌,拿来当椅子坐是伤腰。不是器材的错,是用法。
AI 没有让人变笨。它只是把"理解"从工作流里静默地移除了,移除得如此顺畅,以至于没有人发现它在消失。如果你用的是抄写员模式,那移除的过程,你根本感觉不到,因为它本来就是默认的。
结尾:怎么给自己建认知账本
讲到这里,你大概已经猜到我要说什么了。既然认知债没有账本,那解决办法就是:自己给自己建一本。
不需要复杂的工具,不需要新的流程,三件事就够。
第一笔:给 AI 写的东西写注释,但写的是"为什么",不是"是什么"。 代码是 AI 写的,“是什么"它已经替你说清楚了。你唯一能补充的,是它永远不会写的东西:为什么这么设计,这个方案放弃了什么。一句话就够:“这里用固定窗口,因为请求分布均匀,滑动窗口的复杂度不值得。” 这句话的价值不在于给别人看,在于写这句话的那一刻,你被迫理解了一次。

第二笔:用"解释测试"代替"运行测试”。 每次让 AI 生成代码,提交之前,把 AI 给你的解释文字关掉,自己看着代码,把它讲一遍,不是讲给 AI,是讲给空气,或者讲给你自己。讲不出来的地方,就是你的认知债所在。讲不出来,就回去问 AI,直到你讲得出来。这个测试不花时间,五分钟,但它是唯一能检测认知债的工具。
这个测试有个前提得说清楚:它只能测出"你讲不出",测不出"你讲得对"。一个人可能把错误的理解讲得头头是道——那是另一种债,叫错误自信,比"讲不出"更隐蔽。解释测试不是万能药,它只是把"理解"从默认路径上拉回来一次,让你有机会发现问题。发现了,才算开始还债。
我把它叫"解释测试",因为它本质上是在给你自己上一道和编译器对等的门禁。编译器检查代码能不能被机器执行,解释测试检查代码能不能被人类理解。两道门禁都过了,这段代码才真正属于你。
第三笔:定期"对账"。 每两周,挑一段 AI 写的、你从没细看过的代码,花半小时读懂它,然后删掉一句你认为没用的代码,跑测试——对账,就是查账本上有没有没记的债。如果测试挂了,你就知道那句话为什么在那里。这是你能给自己的、最接近真实还款仪式的体验。
这件事有个反直觉的好处:删掉一句没用的代码、测试却过了,意味着你找到了一处"AI 写得比你需要的多"的地方。这通常意味着那个模块可以精简,也意味着你的理解又多了一块。如果测试挂了,你就找到了那行代码存在的理由。这也是理解。无论哪种结果,你都在还债。
这三笔账,本质上是同一件事:把"理解"重新放回工作流。 AI 不欠你什么,是你默认接受了不验证。改掉默认,债务就不会滚雪球。
最后说一句可能让你不舒服的话。
这篇文章真正想讲的,是我们自己的问题,而非 AI 工具的问题。AI 只是把"跳过理解"从成本极高的主动选择,变成了零成本的默认路径,它放大了我们本来就有的偷懒倾向,仅此而已。责怪 AI 不会让你变强,承认这一点然后改掉默认,才会。
给代码建账本的工具很多,给认知建账本的工具只有一个:你在点击"提交"之前,多问自己一句:这段代码,我真的懂吗?

如果答案是否定的,那你欠的债,就从这一秒开始计息了。
附录:实验代码和原始数据
本文的 E4 实测(AI 生成限流器的意图缺失分析)代码已开源:
GitHub:zhiyulab-evidence/ai-coding-cognitive-debt
code/e4-intent-gap/— 限流器源码(AI 生成,保持原样)+ 生成 Prompt + 意图缺失分析 READMEoutput/e4-intent-gap/— 运行输出与结论
每个子目录都有独立 README,说明如何复现。二进制编译产物不入库,跑实验前自己 go build。
原文发布于 止语Lab