在上世纪六十年代末,随着系统规模增长到开发者已无法有效掌控的程度,“软件危机”这一说法首次出现。此后,每一代人似乎都用更强大的工具“解决”了这场危机,但结果往往只是制造出了更大的问题。
Netflix 工程主管 Jake Nations 表示,如今,AI 正在把这一循环加速到一个新的阶段:无限软件危机。由 AI 生成的代码库,本质上是生成它们的那一连串曲折对话的映射。每一次澄清、每一次方向调整,都会被直接固化进系统架构中。我们正在用“氛围编程”(vibe coding)的方式,一步步走向灾难。
我们正在交付自己并不真正理解的代码
Jake 坦言自己曾交付过一些并不完全理解的代码。这些代码由 AI 生成,测试通过,上线也没问题,但具体如何运作却说不清楚。他认为,这种情况在如今已是普遍现象。
这种现象的背后,源于历史循环、概念混淆,以及最重要的——将“思考”外包。Jake 指出,过去几年在 Netflix 推动 AI 工具落地的经验表明,生产力加速是真实的,但问题也紧随而来:大型生产系统总会以意想不到的方式出故障。当问题发生时,你必须清楚自己正在调试的代码是如何工作的。而现实是,我们生成代码的速度已经远超理解它的速度。
软件危机,一个不断重演的循环
历史总是惊人地相似。上世纪60年代末,顶尖的计算机科学家们便判断社会正经历一场“软件危机”。社会对软件的需求激增,但开发能力却跟不上,项目超期、效率低下。
Edsger Dijkstra 曾有一个著名的观察:当计算机性能大幅跃升后,编程本身反而变成了一个巨大的问题。硬件能力的提升催生了更庞大的软件需求,最终压力全部落到了程序员身上。
这个循环从未停止:
- 70年代:C 语言让我们能构建更大的系统。
- 80年代:个人电脑普及,人人可编程。
- 90年代:面向对象编程盛行,继承层次变得异常复杂。
- 21世纪初:敏捷开发、冲刺、Scrum 成为主流。
- 后来:云计算、移动开发、DevOps 兴起,软件“吞噬世界”。
图1:技术发展如何不断引发更大的问题

如今,AI 彻底改变了规模。Copilot、Claude、Cursor 等工具让代码几乎可以瞬间生成。模式未变,但规模已趋向无限。
核心难点从来不在“写代码”
Fred Brooks 在《没有银弹》中指出,不存在任何一种单一技术能在软件生产率上带来数量级提升。因为真正困难的部分,从来不是敲代码这个机械动作,而是理解问题并设计出正确的解决方案。这是任何工具都无法替代的。
那么,为什么经验丰富的工程师也会写出(或生成)自己看不懂的代码?Jake 认为,答案在于我们混淆了两个概念:简单和容易。
图2:“简单”与“容易”的核心区别

Clojure 语言创造者 Rich Hickey 对此有精辟区分:“简单”关乎结构——单一、无纠缠、职责清晰;“容易”则关乎便利——触手可及、可复制粘贴、安装即用。
简单需要思考和设计,而“容易”唾手可得:安装一个包、让 AI 生成、从 Stack Overflow 复制一段代码。人类天生会选择容易的路。但每一次选择“容易”,都是在用当下的速度换取未来的复杂度。
过去,这种权衡尚可接受,复杂度积累较慢,我们还有机会重构。但 AI 打破了平衡,它将“容易”推向了极致,以至于我们几乎不再考虑“简单”这条路。
对话式AI,正在放大系统复杂度
既然代码可以瞬间生成,为什么还要思考架构?通过对话一步步生成代码看似自然,却极易将一个简单任务演变成一团乱麻。
例如,为一个应用添加 OAuth 认证。你告诉AI:“加一个OAuth流程”,它生成了一个干净的 oauth.js。随着需求迭代(“再加一种OAuth流程”),很快出现了 oauth.js 和 oauth2.js,会话管理开始冲突。
到第二十次迭代时,你已不是在“讨论设计”,而是在被迫管理一个极其复杂的上下文。代码库里充斥着死代码、为通过测试而打的补丁、以及来自不同解决思路的残留片段。AI 会机械地满足你的最新指令,但不会对糟糕的架构决策产生任何阻力。技术债在 AI 眼里并非“债”,只是更多的代码模式。
Fred Brooks 将系统复杂度分为两类:
- 本质复杂度:问题本身的固有难度(如用户支付、订单履约)。
- 偶然复杂度:实现过程中叠加的临时方案、过时的抽象、防御性代码。
在真实代码库中,两者往往纠缠在一起。分离它们需要对历史、上下文和经验的深刻理解。而 AI 并不会区分这些,它会将所有看到的模式一并保留。
AI真正需要的不是“更好的提示词”
Jake 分享了一个 Netflix 的真实案例。他们有一个系统,在旧授权代码和新 OAuth 系统间有一个适配层。用 AI 直接重构看似可行,实则不然。
旧代码耦合严重:权限检查穿插在业务逻辑中,角色假设嵌入数据模型,OAuth调用分散在数百个文件。AI 在重构几个文件后就会遇到无法梳理的依赖,要么放弃,要么更糟——试图用新系统重新实现旧逻辑,导致进一步混乱。
问题在于,AI 无法“看到”表象之下的结构,无法区分业务逻辑和授权逻辑的边界。当偶然复杂度高到这种程度时,AI 不仅帮不上忙,反而可能添乱。
但人类可以区分。我们知道哪些模式是核心,哪些只是历史遗留方案。关键在于,我们需要提前投入时间去厘清这些上下文。
三阶段方法论:在AI时代保持理解
面对庞大的代码库(例如 Jake 提到的百万行Java代码),直接将所有内容扔给 AI 收效甚微。他总结出一套“三阶段方法论”:
-
研究阶段
- 目标:梳理现有系统。提供所有相关上下文(架构图、文档、对话记录),让 AI 代理分析组成部分与依赖关系。
- 过程:这是一个迭代过程。不断追问、纠正、补充信息,直至分析精细化。
- 产出:一份完整的研究文档,说明系统现状、连接关系及改动影响。此处必须设置人工检查点,与现实系统核对,这是性价比最高的防错环节。
-
规划阶段
- 目标:制定一份极其详细的“照着做”实现计划,包括代码结构、函数签名、类型定义、数据流等。
- 价值:大量关键的架构决策在此完成。确保业务逻辑正确、服务边界清晰、避免不必要耦合。计划应清晰到初级工程师也能无误执行。
- 优势:评审速度极快。可以在几分钟内验证整个方案,跟上代码生成的速度。
-
实现阶段
- 目标:基于清晰的研究和计划,进行代码实现。
- 效果:当 AI 有一份具体、清晰的规格说明时,其输出会保持干净和聚焦,避免了冗长对话带来的复杂度螺旋。
这种方法的核心在于:将所有困难、需要动脑的思考工作(研究、规划)前置并完成。AI 则被用于加速后续机械性的实现工作。你可以让智能体在后台执行计划,而你只需回来检查它“是否遵循了计划”,而不是去理解它又“发明”了什么。
软件,终究是一项需要人类智慧的事业
Jake 强调,通过测试的代码与能在生产环境长期稳定运行的代码是两回事;今天能跑的系统与未来能被安全修改的系统也是两回事。
当 AI 能在几秒内生成数千行代码时,理解它们可能需要数小时甚至数天。更危险的是,每一次为了跟上生成速度而跳过思考,我们都在丧失一种关键能力:识别问题、感知“这里开始变复杂了”的直觉。这种模式识别能力来源于经验,而 AI 不会内化来自失败的教训。
三阶段方法论正是为了弥合生成与理解之间的鸿沟。它将“理解”压缩成一系列可快速审查的产出物。
AI 改变了我们编写代码的方式,但并未改变软件失败的根本原因。每一代人都会遭遇自己的软件危机。答案不在于下一个神奇的工具,而在于重新铭记:软件始终是一项人类的事业。最困难的部分从来不是敲代码,而是知道该敲什么。
最终能走得更远的,不会是生成代码最快的人,而是那些仍能理解自己所构建之物、能看清系统脉络、并敢于质疑问题本身的人。
当 AI 写下大部分代码时,我们是否仍然理解自己的系统?这是留给每个开发者的核心问题。
本文探讨了在算法与数据结构设计中如何区分本质与偶然复杂度,更多深入讨论可前往 云栈社区 的算法/数据结构板块交流。本文对软件工程历史的回顾,也涉及计算机基础知识的演变。