AI 编程正在重蹈人类的覆辙
最近大半年,我几乎每天都要花四五个小时跟各种AI编程工具打交道——从补全类插件到能独立跑完整任务的Agent都有涉猎。起初确实很兴奋,感觉像是多了个不知疲倦的结对程序员。但用得越深,一个场景就越来越清晰:我在代码评审里看到的那些AI生成的"杰作",总让我想起十年前刚入行时自己写出的那些自以为很酷、实际上让同事骂街的代码。
这不是巧合,这是一条正在重演的老路。
人类编程花了几十年才学会的那些教训——写过面条代码才知道要结构化,搞出软件危机才知道要工程化,经历过依赖地狱才知道要管理依赖——AI编程正在以另一种形式、更快的速度,把这些坑全部重新踩一遍。这篇文章我想聊聊我观察到的具体现象、背后的原因,以及作为一线开发者我们还能怎么补救。
1. 人类当年是怎么一步步"学乖"的——先看清我们踩过的坑
在讨论AI编程之前,有必要先把人类自己那段黑历史摆出来。因为你会发现,AI现在的行为模式跟早期人类的编程方式惊人地相似。
早期程序员几乎没有任何规范和约束。GOTO语句满天飞,代码执行到哪跳到哪,整个程序像一团打了结的毛线球。后来业界发现这种写法根本没法维护,才逐步形成了结构化编程的概念——顺序、分支、循环三大结构搞定一切逻辑。再到后来,面向对象、函数式、设计模式、分层架构,每一次进步几乎都是因为前面那种写法带来了惨痛的教训,我们才被迫往前走了一步。
上世纪六十年代末有一个很著名的软件危机,核心症状就是:软件规模一大,开发周期就彻底失控,预算超支、Bug多到修不完、项目直接烂尾。这个问题不是一两个团队遇到的,而是整个行业普遍性的危机。正因为痛得够狠,才催生了后来的一整套软件工程方法论——需求分析、系统设计、模块化开发、测试体系、代码评审,全部都是被现实毒打过之后沉淀下来的东西。
再说一个程序员都有切身体会的坑:过早优化。我刚工作那会儿特别迷恋各种奇技淫巧,写个遍历非要搞个位运算,写个数据组装非要上一个复杂的设计模式。当时觉得自己可厉害了,但过两个星期自己回来看这段代码都得琢磨半天,更别说别人。后来才明白一个朴素的道理:代码首先是写给人看的,其次才是给机器执行的。可读性、可维护性、结构清晰,这些不是软性指标,而是真正的生产力。
还有一个更深刻的教训是全局一致性。人类程序员在大项目里特别容易顾此失彼——改了一个模块的接口,忘了同步其他模块;新增了一个配置项,忘了更新全部调用方。这个问题本质上是因为单个程序员的一次性工作记忆是有限的,而大型系统的信息量远超个体所能承载的上限。为了对抗这个问题,才发展出了接口契约、自动化测试、CI/CD这些工程手段,用流程和工具去兜住个体的认知瓶颈。
我之所以要把这些旧账翻出来,是因为等一下你会发现:AI编程当前面临的绝大多数问题,几乎都能在人类编程历史上找到原型。它正在用一种新的皮囊,装着旧时代的灵魂。
2. 现场观察:AI写出的代码,正在复刻当年的"面条代码"
先说我自己的一个典型经历。上个月接了个需求,要把一个内部数据同步模块重构一下,支持多租户的数据隔离。我用了个AI编程助手,给它描述了需求,它很快生成了整段同步逻辑。第一眼看过去挺像那么回事——函数命名清晰、有类型标注、还有注释。但等我把代码完整读进去,越看越不对劲。
整个模块的主控流程里,正常的业务分支之间,塞了三个异常处理分支和两个重试逻辑,它们之间还有互相跳转。某个工具函数的返回值在成功时是对象,失败时返回null,但调用方一个地方用了null检查,另一个地方却直接去访问属性,靠的是外层try-catch兜底。最关键的是,AI自己生成的一个缓存策略方法,在三天后的另一个需求里,被我自己手动调用过一次,结果它内部竟然还在递归调用同一个方法——等于平白多了一层无意义的堆栈。
这种代码如果让新手写出来,评审时会被打回去重写。但问题是,它是AI生成的,而且生成得异常流畅、异常自信。我后来在几个技术社区聊了聊,发现整个现象非常有普遍性:
- AI擅长生成"局部合理、整体失控"的代码。单看任何一个函数都有模有样,但拼在一起,模块之间缺乏统一的设计约束,像是不同的人在不同时间各写各的。
- AI极度偏爱"防御性过度"。它会在每个可能为null的地方做判断,在每个循环里都套一个状态检查,最终代码行数膨胀到人类无法轻易审阅的规模。
- AI对上下文窗口之外的内容完全无感。它只认识它看到的那段代码和对话,如果这个模块有历史包袱、有既定约定,而你没有在一开始就告诉它,它就会凭自己的"经验"自由发挥,大概率跟整个系统的风格是脱节的。
这像什么?就像所有人都不写注释、不用版本管理、想在哪跳转就在哪跳转的远古时代。AI编程确实规避了人类在打字速度、命名疲劳上的限制,但它同时也以极高的效率,生产出了类似形态复杂、结构脆弱的面条代码。以前一个程序员写面条代码,祸害的是一个模块;现在AI以每小时几千行的效率生成面条代码,祸害的是整个代码库。
更讽刺的是,当我去问AI:"这段代码会不会过度设计了?"它会非常礼貌地承认:"你说得对,这里逻辑确实有点复杂,可以简化。"它知道什么是好的代码,但它默认不会主动给你好代码,只给你"最像好代码"的东西。这个区别非常关键。
3. 你以为它在"思考",其实它在"模仿"——AI重蹈覆辙的根因剖析
人类程序员写出烂代码,通常是因为经验不足、沟通不到位、或者时间压力太大。AI写出类似的烂代码,根因跟人完全不同,但表现出来的症状几乎一模一样。要理解这件事,得先拆一下AI编程工具的工作范式。
当前的AI编程模型本质上是"下一个Token预测"。它并没有一个真正的需求模型,也没有一个对整个项目和系统架构的全局认知。它的工作方式是:根据你当前的代码上下文、你之前说的话、以及它从海量代码库里学到的统计规律,生成一个在概率上最接近"人类程序员会写出来的代码"的序列。
问题就出在这个"最接近人类程序员"上。它从训练数据里学到的"人类的平均水平",不是"人类工程实践最佳水平"。海量开源代码里固然有很多优秀范例,但也有大量业余项目、教学Demo、论坛贴子里的碎片代码,还有各家公司风格迥异的内部实践。AI做的是把这些全部平均化,得出一个"看起来最普遍"的写法。
这就解释了为什么AI经常会给出过度工程化的方案。因为在它见过的庞大语料里,面对"用户登录"这个场景,有人用简单函数实现,有人引入完整的权限框架,有人套三层架构加DTO转换,还有人顺手把观察者模式塞进去。统计上,出现在各种博客和技术文章里的"复杂方案"占据了更高的权重,因为这些内容更常被写成教程并被收录进训练集。于是AI在"平均"之后,更倾向于输出一个看起来架构完整、涉及N个类和接口的方案,而不是一个最短的、能跑的、容易维护的写法。
人类失去全局视野,是因为大脑工作记忆有限;AI没有全局视野,是因为它的上下文窗口有上限,且它的训练目标根本不包含"维护一个长期稳定的架构演进"这类目标函数。你给它的上下文里只放了一个文件的代码,它就只能基于这一个文件做决策;你给它放了整个项目,它能着眼的时间范围也就是这个项目当前快照的短时范围。它不会像资深工程师那样,在动手前先在脑子里过一遍"这个模块未来半年可能要扩展三个方向,所以现在接口要收敛一点"。它看到的是"当前这个任务",不是"这个系统的未来"。
这背后还有一个更让人忧虑的事实:AI训练的目标函数是"生成的代码通过测试的概率",或者是"与人类偏好对齐的程度",但它从来就不是"代码在两年内可维护的总成本最低"。换句话说,它在做一个非常短视的优化——让当前的对话、当前的任务得到最顺利的完成。至于这个完成动作是否会破坏系统的整体一致性,是否引入了隐性的架构债,是否让后来维护的同事想骂人,这些因素在当前的训练体系里几乎无法被有效建模。
所以,AI已经踩上的坑不是偶然,而是源于范式本身。它没有一个"全局的、持续演进的软件系统的内部模型"。在这个意义上,它甚至比当年的人类新手还要脆弱——因为它会非常流利地把错误包装得像是正确。
4. 最该警惕的不是它写错,而是它"太流畅"——当能力幻觉撞上工程现实
很多开发者对AI编程最大的误解,是觉得"它写得快了,所以我也快了"。在简单任务上这是成立的。一旦任务复杂度上来,"快"反而会变成最大的风险来源。
我做一个内部的接口联调时遇到过一个很诡异的案例。AI生成了一个新的支付回调处理函数,逻辑看起来完整,字段校验、签名验证、幂等判断一应俱全。我当时赶进度,大概扫了一遍就合进去了。上线当天晚上,凌晨两点告警响起来——某个渠道的回调被重复处理了三次,产生了三笔重复入账。我登录上去排查,整整花了三个小时才定位到原因:AI生成的幂等判断用了一个"刚刚好不在它自己生成的代码里定义"的Redis Key前缀,而那个前缀是我在另一个被AI当作参考却理解错了语义的旧模块里使用的。两边用的字符串拼接方式微妙地不同,导致幂等键永远不命中。
这个错误的可怕之处不在于"有Bug"——人写的代码也有Bug。而在于它误导性的流畅感:签名验证完,就给人一种"剩下的一定没问题"的错觉。它没有给出任何"这里的幂等设计我认为需要再确认一下"的信号,而是以一种极其自信的方式完成了整段逻辑。这种"流畅的自信"是AI输出风格中最有迷惑性的部分。
在工程实践里,我们通常会通过编码规范和评审制度来防范人为失误。可AI生成的代码在格式上太规整了,命名规范、换行漂亮、注释齐全,它天然会降低评审者的警惕性。你看到一份格式完美的PR,第一反应是"这代码作者很清楚自己在做什么",但实际可能完全不是这么回事。
更要命的是,AI还会回避"承认不确定性"。如果你在对话里问它"这段代码有什么风险吗?",它往往会列几个风险点。但如果你不主动问,它绝对不会主动把"我这里其实拿不准"挂在嘴边。人类程序员在不确定的时候会说"这块我不太确定,你再看看",但AI的奖励机制决定了它会被训练成倾向于给出一个确定的答案,而不是一个诚实的答案。
这就构成了一种危险的循环:代码越流畅,人工评审越松懈;评审越松懈,缺陷越晚被发现;缺陷越晚被发现,修复成本就越高。而成本最高的那些缺陷,往往不是写错的逻辑,而是那些"看起来完全正确、但放在整个系统里就是不对"的语义错位。这恰恰就是当年软件危机的另一个翻版——不是没人写代码,而是没人能hold住所有代码。
5. 不让AI重蹈覆辙的三个关键动作——从工程管理视角给AI套上缰绳
说了一堆问题,总得给点出路。我自己实践下来,AI编程是可以安全使用的,但它不能替代工程管理,它必须被纳入工程管理体系里。有三件事是最关键的。
先给系统定"宪法"。很多AI编程工具都支持自定义指令或项目级上下文文件,类似AGENTS.md、CLAUDE.md这类项目说明书。这是当前约束AI最有效的手段。我在每个项目里都会维护一份详细的"项目约定"文档,里面写清楚:这个项目的架构分层是怎么样的、哪些目录放什么职责、接口命名的历史约定、不允许引入哪些重依赖、数据访问统一走哪一层、日志规范是什么。每次让AI干活之前,我都会在上下文里带上这份约定。实测下来,这么做之后AI代码风格偏离项目的概率大幅下降。这跟团队里给新人发一份开发规范文档是同一个逻辑——你不能指望新人天生就知道这些约定。
把代码评审的强度提到最高。AI生成代码的正确评审状态不是"扫一眼",而是"逐行读"。我给自己定了一个规则:AI生成的代码,我必须能向自己解释清楚每一个分支存在的理由,解释不清楚的地方一律打回去让AI重写或者自己手动改。尤其是异常处理、缓存、并发和异步逻辑,这几个领域几乎就是AI幻觉的重灾区。如果你在一片AI生成的代码里看到一个"防御性的判断",先问一句:这个判断真的有必要吗?它是为了防什么?如果AI答不上来,这段代码大概率是它在"模仿防御"而不是"真正防御"。
把任务切到AI能处理的最小粒度。现在的AI编程工具上下文窗口再大,也扛不住把整个大型系统塞进去让它做设计决策。我现在的做法是:架构设计、模块拆分、接口定义这些事永远由人来做,AI只负责实现已经定义清楚的小粒度函数、组件、数据处理逻辑。每个任务的描述里我会写清楚输入输出、边界条件、性能要求、禁止事项。这本质上就是在给AI一层"结构化编程"的约束——就像当年人类为了对抗自身认知局限而发展出来的模块化和分工一样。你让AI负责的粒度越接近"函数级",它产出物的可控性就越高;你让它负责"系统级",它大概率会交给你一坨很流畅的灾难。
我后来复盘那个幂等Bug的时候,最大的教训不在于"AI写错了",而在于"我没有给它足够的上下文,也没有对关键路径做足够的审查"。我把一个涉及资金的关键逻辑,完全是当作一个辅助编码任务交给它,然后自己跳到了下一个任务上。这在流程上是不可接受的。AI可以作为强大的执行者,但绝对不能成为他人工程判断的替代品。
6. 把"反思"当作一种工程能力:真正长期的解法是教会AI理解债务
最后的这一点,是我认为整个话题里最值得展开的。有人说,AI可以无限迭代、无限修正,所以它能自己摆脱这些坑。但我在实际使用中,对此持审慎态度。
AI在一个单次会话里确实能"纠正错误"。你告诉它"刚才的代码过度设计了",它可以立刻给你一个更简单的版本。但这种纠正是有条件的——它必须被显式触发。你指出它才改,你不指出它就一路狂奔。这跟人类程序的"反思能力"有本质区别。资深工程师在写代码之前,脑子里会自动过一遍这个方案的潜在债务和长期成本,这是一种内化的、前置的反思。而AI的反思是被动的、后置的、由用户指令驱动的。
换句话说,AI当前没有"架构审慎性"的内置机制。它不是不想给你好设计,而是它不知道你的项目已经积累了多少技术和架构债,不知道你团队的风格偏好,不知道某个看似灵活的抽象会在未来的哪个需求里卡住你。所有这些信息要么需要你手工喂给它,要么需要你用严格的项目宪章去约束它。一旦这些外部机制缺失,AI必然会滑向它最舒适的路径——生成一个看似合理、实则平均化的方案。
所以,如果把"AI编程重蹈人类覆辙"这个问题再往前推一步,我的结论是:它重蹈的不是某几条具体的错误,而是"没有反思机制、纯靠试错、用短期目标驱动决策"这个软件工程早期阶段的内核。人类是从无数次线上事故和维护地狱里才被迫成长出工程化能力的。AI的成长则需要我们主动把工程化的约束注入它的工作流程里。
这也是我个人认为现阶段使用AI编程最重要的一条心法:不要把AI当成一个“不用管过程的自动完成机器”,而是要当成一个“能力极强但严重缺少大局观的新人”。你要给它定义边界,你要给它说明上下文,你要在它交出结果后认真评审,你要把团队共识写进它必须读取的文档里。这套流程和当年带新人的流程几乎一样,只不过这个新人的产出速度是人类新人的几十倍,所以流程的颗粒度必须更细、把关强度必须更高。
AI编程确实把我们的生产力天花板抬高了一大截。但也正因为如此,它把所有错误的发生速度也放大了同样的倍数。人类当年能从软件的混沌时代走到工程化时代,靠的是一整套约束机制和反思习惯。现在轮到这个新兴工具了,它走过的弯路和我们将要补上的约束,本质上就是同一场课程的上半场和下半场。我们这些坐在评审席上的人,责任就是确保下半场不会重演上半场所有的灾难。