AI编程悖论:为什么用AI写代码可能让你变得更弱?
想象一下:你用了半年AI编程助手,代码写得飞快,但是当你突然遇到 bug,但是一时半会无法使用 AI 的时候,会发生什么样的事情?
前段时间 Anthropic 的研发团队发布了一篇非常有意思的文章 How AI assistance impacts the formation of coding skills ,主要解释了一个问题:AI 写代码能让你更快交付,但会不会让你“学得更慢、会得更少”?
文章说了什么
官方设计了一套随机对照试验,具体实验的过程不过多阐述,说一下结论.
- 使用 AI 变成的程序员在后续测验中平均得分比手动编码组低17%.
- 使用 AI 的程序员完成任务的时间平均快了 2 分钟,但是差异并不算显著;
- 在调试、代码阅读和概念理解方面的差距最大;
- 最重要的一点:通过提问、请求解释、提出概念性问题等方式主动使用AI的程序员,比仅仅用AI生成代码的人保留了更多知识。
通过这些结论,推导出了一个很有意思的悖论:“越依赖 AI,越缺乏监督 AI 的能力”.
那接下来,结合这篇文章和我最近一年使用 AI 辅助编程的一些经验,写一些我自己的经验和教训.
一.不要把思考过程完全交给 AI
先回答开始提到的那个问题:AI 写代码能让你更快交付,但会不会让你“学得更慢、会得更少”?
答案是:如果不能正确的使用 AI ,确实会让你“学得更慢、会得更少”.
AI 很容易让我们产生“认知卸载”(Cognitive Offloading)的依赖。一旦习惯了只输入需求、复制粘贴代码,我们的大脑就会停止对代码逻辑、边界条件和系统设计的深度思考。
在使用 AI 进行辅助编程的时候,要提前思考,先在脑海中或者纸上画出大致的逻辑结构,然后把这个逻辑结构,反复的和 AI 进行讨论和修改.
千万不要让 AI 帮你“设计”一切,而是让它帮你“实现”你的设计,要保证程序员的主体性.
然后当 AI 开始输出代码的时候,要保持 Reviewer 的心态,对待它提交的代码(Pull Request),你必须逐行阅读、理解,并问自己:“如果有 Bug,会在哪里?”、“这里的内存管理安全吗?”。
接着在代码进入调试阶段的时候,要多问自己和 AI,问”为什么”.比如:“为什么要用这个库?”、“有没有更好的替代方案?”、“这段代码在并发场景下安全吗?”。
举个例子
错误模式:
"帮我写个排序算法"
正确模式:
"我需要实现一个稳定排序,数据量大概1万条,内存敏感场景。我先想想...(我的思路),你觉得这个思路有什么问题?如果用快速排序,最坏情况怎么避免?"
二.好好写一份 AGENTS.md
AI 的上下文窗口(Context Window)虽然越来越大,但它依然需要明确的“游戏规则”才能表现得最好。与其每次都重复你的偏好,不如把项目规范固化下来。
AGENTS.md 的作用,主要是可以 明确项目的代码风格(如命名约定、目录结构、使用的库版本,”优先使用组合而非继承”);以及把项目中容易踩的坑、特殊的业务逻辑记录在案,让 AI 在生成代码时能参考,避免重蹈覆辙。
多在子目录写 AGENTS.md
各种 AICoding 往往都内置了一个机制:它读某个目录的文件时,会自动把那个目录的 AGENTS.md 也拉进来。
这实际上就是 LazyLoad 的思路,按需加载;只有真正接触相关代码时才会读对应配置,不会一上来就把所有规则都塞进上下文。
只在主目录中记录最关键的规则
AI 的关注力或者说上下文是非常宝贵的,尤其是对于 AGENTS.md 这种每次都要阅读的文件.
这里最好要小于 100 行,最多最多不要多于 200 行.如果多于这个数字,就要考虑到是不是要拆分规则了.
小心降权和忽略
在上下文过长的时候,有时候 AI 是会偷偷的忽略一些 AGENTS.md 里的内容,以至于有时候会发现在对话过程中,会莫名其妙的少一些明明已经写好的规则.
这里我推荐一个”金丝雀测试法”,我在 AGENTS.md 中,开头加入了一句”> 请称呼我为 伯阳大人”,结尾加入了一句”请在每个回答结束后用”喵”结尾”.
对于其它工具的态度
这里多插一句,虽然我也在使用各种 MCP 和 Skills,但是我在大项目中,实际上是尽量减少使用这些的.因为它们过于复杂和受各种情况影响(网络或者各种奇葩原因).在这里,反倒是不如细心的维护一套 AGENTS.md 文件,反而更加稳定和安全.
三.当心 AI 一本正经的胡说八道
AI 模型(尤其是 LLM)本质上是基于概率的预测机,它们很容易产生“幻觉”(Hallucination),即自信满满地胡说八道。
前段时间我在看网络相关的东西,某个刚刚进行了大更新的 AI,就给我输出了一个错的离谱的回答

只能说 AI 确实有点”幽默”了.
对于这种情况,我认为对于 AI 提供的 API 用法或生僻的库,务必去官方文档或源码中核实,不要盲目相信;或者让 AI 解释它的代码逻辑,并要求它提供参考来源。如果它无法解释清楚,或者来源不存在,那很可能是幻觉。
在开头提到的那个实验中,还有一个小结论:Anthropic 的研究发现,AI 辅助下,调试能力和概念理解能力最容易下降。因此,在这两方面要格外警惕,不要让 AI 剥夺了你排查问题的机会。
要对抗 AI
一个人(或一个模型)很容易陷入思维定势。利用 AI 的多角色扮演能力,可以让它自己查漏补缺。
我一般这样做:
- 开始在讨论程序设计时,让 AI 反问你需求中的漏洞、缺失的异常路径、性能瓶颈;
- 让 AI 生成代码后,再开启一个新的会话(或让它切换角色),扮演“苛刻的代码审查员”,对刚才生成的代码进行 Review;
- 让 AI 出题考你,或者让你解释某段代码,然后它来纠正你的理解偏差;
- 或者直接换一个 AI,对回答进行审查,比如说”这个回答中存在错漏,你找出来”.
这里举个前段时间的例子:上周我用AI写了个WebSocket重连逻辑,AI给的代码看着完美,但我多问了一句’如果服务器返回401怎么办?’,AI才意识到需要处理认证失效场景…”
四.在大型项目中冷静对待 AI Coding
虽然 AICoding 的能力在不断变强,但是我们依然要冷静的使用.
对于一个项目来说,不断的开发是一个熵增的过程.每一个增量都在增加复杂性;尤其是有着复杂的项目依赖和代码风格限制的项目.
这种复杂性非常容易积累,但是却极难消除.这种情况在使用某些使用动态派发的语言(没错,我说的就是 Objective-C),非常容易发生一些奇怪的 bug,并且对于复杂的 API 版本变动(没错,说的就是你, iOS),堪称灾难.
这样就会造成一种结果:每一次变更造成的影响指数型变大,看似简单的变更需要在多个不同层级的地方修改;其次,开发者甚至于 AI 本身都会花费越来越多的时间来掌握上下文.
五.保持自身的学习
很多人低估了 AI 在帮助程序员学习上的能力.
我现在就很喜欢利用 AI 辅助我去阅读源代码,以及各种概念.我主要是进行”摩擦式学习”,反复的和 AI 对话讨论概念,并从不同角度阐述自己的理解,反驳 AI 的结论;或者让 AI 对我进行提问,通过我的回答来验证我的理解.
我们要避免程序员单方向向 AI 提问.要自己向自己提问,还要让 AI 问 AI,乃至AI 向程序员提问.
这里举个例子:我前段时间在阅读 webkit 源码,我先提问”ResourceLoader::start() 是在什么时机被调用的?它的调用栈通常长什么样?”?然后我阐述一些我自己的观点,然后询问”我说的对不对”,反复执行这个问->答->阐述观点->评判的过程,以便快速的学习.
六.使用 rust 这种强势编译器语言
我现在在使用 AICoding 的时候,会在可选择的情况下尽量使用 rust.
首先,rust 的很多机制,比如说借用检查、类型系统、生命周期、 Result / Option 让很多潜在错误在编译期暴露.
同时,编译器能当“高质量的静态审查器”,快速告诉你哪里违反了安全或类型规则。AI 生成的代码往往能跑通逻辑,但容易忽略内存安全或并发安全,Rust 的编译器正好能卡住这些问题。
换句话说,既然 AI 容易导致调试能力下降,那么使用一门“编译通过即大概率正确”的语言,可以减少运行时莫名其妙崩溃的概率,强迫你在编写阶段就理清逻辑。
这种模式下,AI 生成 -> 编译器报错 -> 把错误喂给 AI -> AI 修正。这个循环比“运行 -> 崩溃 -> 猜测 -> 调试”要高效且安全得多。
结论
作为在 AI 浪潮下艰难前行的程序员,我们不应该抗拒时代的大潮,但也不应该放弃思考.
不应该只把 AI 当作“代码代写器”,而是当做“解释器+校对员+学习伙伴”,多问”为什么”,少问”怎么做”则可以更加安全高效的使用,以及提升自我的能力.
毕竟自动驾驶确实非常好,但是千万不要忘记握方向盘.