配套视频:https://www.bilibili.com/video/BV1zB64BkEyc/

引言:一个不得不说的秘密

"大家好,我先坦白一件事。"Netflix 的工程师 Jake Nations 在演讲开场就抛出了一个令人不安的confession:"我发布了一段自己并不完全理解的代码——用AI生成、测试通过、部署上线,但我无法解释它是如何工作的。"

停顿片刻后,他环视台下:"关键是,我敢打赌你们每个人都做过同样的事。"

这不是个例,而是2024年软件开发的新常态。我们正站在一个历史转折点:代码生成的速度已经超越了人类理解的速度。这不是第一次软件危机,但却是第一次面临无限规模的危机。

第一部分:历史总在重演

1968年的警钟

让我们回到1968年。那一年,一群计算机科学家聚在一起,讨论一个紧迫的问题:软件危机。项目延期、质量低下、需求无法满足——这些问题听起来是不是很熟悉?

Dijkstra 当时说了一句经典的话:"当我们只有少数弱小的计算机时,编程只是个小问题;如今我们有了巨型计算机,编程却成了巨大的难题。"

他的洞察是:硬件性能增长了一千倍,社会对软件的需求也相应增长了一千倍。程序员必须在方法和手段之间找到平衡,来支撑这种指数级的增长。

危机的循环

这种模式此后不断重演:

  • 70年代:C语言出现 → 我们能编写更大的系统

  • 80年代:个人电脑普及 → 人人都能编写软件

  • 90年代:面向对象编程 → 带来了"继承层级地狱"(谢谢你,Java)

  • 2000年代:敏捷开发 → Scrum Master 和 Sprint 流程

  • 2010年代:云计算、移动互联、DevOps → 软件真正"吞噬了世界"

  • 2020年代:AI、Copilot、Cursor、Claude → 代码随说随有

每一代人都突破了上一代的瓶颈,但也创造了新的复杂性。而这一次,规模变了——它是无限的

"没有银弹"的预言

1986年,Fred Brooks(《人月神话》的作者)写了一篇论文《没有银弹》。他的核心论点是:不会有任何单一创新能带来软件生产力的数量级提升

为什么?

因为编程的难点从来不是代码的机制、语法或打字。真正难的是理解问题本质并设计解决方案。没有工具能消除这一根本困难。

我们创造的每种工具和技术都简化了操作机制,但核心挑战——弄清楚该构建什么、如何运作——依然同样困难。

第二部分:简单与容易的致命混淆

两个被混用的词

如果问题不在机制本身,为何我们仍不断优化它?经验丰富的工程师为何会写出自己都看不懂的代码?

答案在于两个我们经常混淆的词:简单(Simple)和容易(Easy)

Clojure 语言的创造者 Rich Hickey 在2011年的演讲《Simple Made Easy》中精确定义了这两个概念:

简单(Simple)

  • 词源:sim-plex,单一编织

  • 含义:无纠缠、各司其职、互不交织

  • 本质:关乎结构

容易(Easy)

  • 词源:adjacent,邻近的

  • 含义:触手可及、无需努力

  • 本质:关乎距离

举个例子:

  • 复制粘贴 Stack Overflow 的答案 → 容易

  • 理解为什么这个方案有效 → 简单

AI:终极的"容易按钮"

这里是关键:我们不能单靠愿望让复杂变简单,但总能让事情更容易

简洁需要思考、设计和梳理。但让事情变容易?只需放近一点:

  • 安装一个包

  • 用AI生成

  • 从Stack Overflow抄个答案

人性天生倾向走捷径,这是进化的结果。但AI将这条捷径变得毫无阻力

为何还要思考架构,当代码瞬间就能生成?

一个典型的崩溃过程

让我展示这是如何发生的。假设我们要给应用添加身份验证:

第1轮:"添加认证功能"

  • 得到一个简洁的 auth.js 文件

  • 看起来不错!

第5轮:"现在也要添加 OAuth"

  • 现在有了 auth.jsoauth.js

  • 开始有点复杂了

第12轮:"会话好像坏了,有很多冲突"

  • 开始修修补补

第20轮:你已经不是在讨论了

  • 你在管理一个复杂到连自己都记不全的上下文

  • 废弃方法留下的无效代码

  • 被"修复"到能跑的测试

  • 三种不同解决方案的碎片

每条新指令都在覆盖前一条的架构模式:

  • "让认证在这里工作" → AI照做

  • "修复这个错误" → AI照做

  • 对错误的架构决策毫无阻力

代码只是被动适应你的最新请求。每次互动都在选择容易而非简单。而容易总是意味着更多复杂性。

我们明知不该这样,但当捷径如此顺手,我们还是会走。复杂性正在累积,直到为时已晚

第三部分:复杂性的真面目

AI眼中的代码库

AI将简单发挥到了极致:明确需求,即刻生成代码。但这里有个危险:

生成的代码会将你代码库中的每个模式一视同仁。

当代理分析你的代码库时:

  • 第47行的认证检查 → 这是一种模式

  • 那段奇怪的、像GraphQL一样运作的gRPC代码(2019年写的)→ 也是一种模式

  • 技术债 → 不被识别为债务,只是更多代码

两种复杂性

Fred Brooks 在《没有银弹》中指出,每个系统都有两种主要复杂性:

1. 本质复杂性(Essential Complexity)

  • 问题本身的固有难度

  • 用户需要支付、订单必须履行

  • 这是软件系统存在的理由

2. 偶然复杂性(Accidental Complexity)

  • 我们在实现过程中添加的所有其他东西

  • 变通方案、防御性代码、框架

  • 曾经合理的抽象

  • 为了让代码运行而拼凑的一切

在真实代码库中,这两类复杂性无处不在,且相互交织。分离它们需要上下文、历史和经验。

生成的输出不做这种区分。所有模式都被原样保留。

Netflix的真实案例

这是我在Netflix工作中的一个真实例子:

我们有一个系统,在旧的授权代码(大约5年前写的)和新的集中式认证系统之间有一个抽象层。当时我们没时间重构整个应用,所以只是在中间加了个适配层。

现在有了AI,这似乎是个绝佳的重构机会——直接迁移到新系统。看起来只是个简单请求,对吧?

错了。

旧代码与其授权模式紧密耦合:

  • 权限检查混杂在业务逻辑中

  • 角色假定深植于数据模型

  • 认证调用散落在数百个文件中

代理会启动重构,改几个文件后就会遇到"这取决于..."——它无法理清依赖关系,然后:

  • 失控

  • 放弃

  • 或者更糟:试图保留部分旧逻辑,用新系统重新实现

问题在于:它看不到缝隙。它无法识别业务逻辑在哪里结束、认证逻辑从哪里开始。一切都纠缠在一起。

即使有完美的信息,当你的偶然复杂性达到这种程度时,AI也找不到清晰的路径。我发现它只会在顶部添加更多层。

我们能做什么?

我们能分辨差异——至少在我们放慢脚步思考时可以。我们知道哪些模式是本质的,哪些只是几年前某人的解决方式。

我们掌握着AI无法推断的上下文,但前提是我们必须花时间做出这些区分。

第四部分:解决方案——三阶段方法论

面对百万行代码

那么,如何在面对庞大代码库时区分偶然复杂性与本质复杂性?

我负责的Netflix代码库约有一百万行Java代码,主服务大约500万个token。没有任何上下文窗口能装下它。

最初,我想:"或许我可以直接复制大量代码到上下文中,看看AI能否自动弄清发生了什么。"

就像之前的授权重构一样,输出因自身复杂性而迷失了。

我不得不另辟蹊径。

从500万token到2000字

我必须有所取舍:

  • 设计文档

  • 架构图

  • 核心接口

  • 关键组件

然后,我花时间撰写了组件应该如何交互、应该遵循什么模式的详细需求。

我在写规范。

500万个token变成了2000字的说明书。然后,我更进一步,将该规范转化为一组精确的代码执行步骤——没有模糊指令,只有精确的操作序列。

结果?更清晰、更专注、更易理解的代码。因为是我首先定义的,我规划了它的执行。

这就是我所说的**"上下文压缩"**(Context Compression),你也可以叫它上下文工程或规范驱动开发——名字不重要。

重要的是:思考和规划成为了大部分工作。

三阶段详解

让我详细讲解这个方法如何实际运作:

阶段一:研究(Research)

把所有前期资料都提供给AI:

  • 架构图

  • 文档

  • Slack讨论串

  • 尽可能多的相关上下文

然后利用代理分析代码库并绘制依赖关系图。

这不应是一次性过程。我倾向于逐步探查:

  • "缓存是怎么处理的?"

  • "出错时如何处理?"

当分析错误时,我会纠正它。如果缺少上下文,我会补充。每次迭代都在优化分析结果。

输出:一份研究文档

  • 呈现已有内容及其关联

  • 你的修改将影响什么

  • 数小时的探索被压缩成几分钟的阅读

关键检查点:这是将分析与现实对照验证的时刻——整个流程中最具决定性的环节。在这里发现错误,可以防患于未然。

阶段二:规划(Planning)

有了可靠的研究成果,我们制定详细实施方案:

  • 真实的代码结构

  • 函数签名

  • 类型定义

  • 数据流

这个计划应该详细到任何开发者都能遵循。我把它比作"数字填色"——你应该能把它交给最初级的工程师说:"照着做。"即使逐行照搬也能正常运行。

这一步是我们做出关键架构决策的地方:

  • 确保复杂逻辑正确

  • 确保业务需求遵循良好实践

  • 确保服务边界清晰、职责分明

  • 避免不必要的耦合

我们能提前发现问题,因为我们经历过。AI没有这种经验——它将每个模式都视为必需项。

这一步的真正神奇之处在于审查速度:几分钟内就能验证此方案,明确知道将构建什么。

为了跟上代码生成速度,我们必须能够同样快速地理解我们正在做的事。

阶段三:实施(Implementation)

现在我们有了清晰的计划和研究支持,这一阶段应该相当简单。这正是关键所在。

当AI有明确的规范可遵循时:

  • 上下文保持简洁专注

  • 避免了长对话导致的复杂性螺旋

  • 只有3个聚焦输出,而非50条演进式消息

  • 每个都经验证后才推进

没有被废弃的方法,没有冲突的模式,没有那些导致处处留下无效代码的"等等,其实不用"时刻。

真正的好处:你可以用后台代理来完成这项工作。因为你已经完成了所有思考和繁重工作,它可以直接开始执行。你去处理其他事,稍后回来快速审查。

你只需确认它符合你的计划,而不是试图判断是否有新内容被发明出来。

方法的本质

关键是:我们并非用AI代替思考

我们用它加速机械部分,同时保持自身理解能力:

  • 研究更快

  • 规划更周全

  • 执行更清晰

但思考、综合与判断——仍由我们负责

第五部分:实战检验

授权重构的续篇

还记得我说AI搞不定的那个授权重构吗?现在我们真正在着手处理,并开始取得进展。

但问题不是我们找到了更好的提示。

我们发现甚至无法开始研究、规划、实施——我们必须先手动做一次。

没有AI,只是:

  • 读代码

  • 理清依赖

  • 进行更改

  • 看看什么会崩溃

说实话,这很麻烦。但它至关重要。

这次手动迁移揭示了:

  • 隐藏的约束

  • 哪些不变量必须保持成立

  • 若认证变更,哪些服务会中断

仅靠代码分析无法发现的问题。

将经验转化为知识

然后,我们将那个手动迁移的Pull Request提交到研究流程中,作为研究的起点。

AI就能看出干净的迁移是什么样子。

但每个实体都有些不同,所以我们必须去询问它:

  • "这个加密的内容怎么处理?"

  • "这个未加密的呢?"

我们必须每次都提供额外上下文,经过多次迭代。

然后,也只有那时,我们才能生成一个可能一举成功的计划。

"可能"是关键词——我们仍在验证、仍在调整、仍在发现边界情况。

核心教训

三阶段法并非魔法。它只有在我们手动完成一次迁移后才有效。

我们必须赢得理解,才能将其编码到流程中。

我仍然认为没有银弹——不是更好的提示、更好的模型,甚至不是写更好的规范。

只是深入理解你的系统的工作,深入到你可以安全地对它进行更改。

第六部分:更深层的危机

"能用"还不够

那么,为何要这么麻烦?为何不直接用AI迭代直到成功?最终模型不会足够强大,直接就能用吗?

对我来说,"能用"还不够

有区别:

  • 通过测试的代码 vs 能在生产环境存活的代码

  • 今天能运行的系统 vs 未来可被他人修改的系统

知识鸿沟

真正的问题是知识鸿沟:

当AI能在几秒内生成数千行代码,理解它却可能耗去你:

  • 数小时

  • 数天

  • 甚至永远(如果真那么错综复杂)

我们正在失去的能力

这是很少有人讨论的一点:

每次我们为了跟上生成速度而跳过思考,我们不只是在添加无法理解的代码——我们正在失去识别问题的能力。

那种直觉,那种在提醒你"嘿,这变得复杂了"的本能——当你不理解自己的系统时,它会萎缩

模式识别的来源

模式识别源于经验:

  • 当我发现危险架构时,是因为我曾在凌晨3点处理过它

  • 当我推动更简单的方案时,是因为我不得不维护过别人的复杂替代方案

AI只生成你要求的内容。它不编码从过往失败中提炼的经验教训。

三阶段法弥合了这一差距。它将理解压缩为可以按生成速度审查的产物。

没有它,我们只是在累积复杂度,速度快于我们理解它的能力。

结论:软件的人类本质

每一代的危机

AI彻底改变了我们编写代码的方式。

但老实说,我认为它并未改变软件本身失败的根本原因。

每一代都面临过自己的软件危机:

  • Dijkstra那一代通过创立软件工程学科来应对

  • 我们这一代面临的是无限代码生成的挑战

真正的解决方案

我认为解决之道不是另一个工具或方法论。

而是记住我们始终明白的事:软件是人类的事业。

难的从来不是敲代码,而是清楚该写什么代码。

未来属于谁?

真正出色的开发者不会是:

  • ❌ 生成代码最多的人

而会是:

  • ✅ 真正理解所构建系统的人

  • ✅ 仍能看清问题本质的人

  • ✅ 能意识到正在解决错误问题的人

那依然是我们,也只能是我们。

最后的问题

我想留下一个问题。

我认为问题不在于我们是否会使用AI——那已是定局,船已起航

对我来说,真正的问题是:

当AI编写了大部分代码时,我们是否还能理解自己的系统?


实践指南:七条行动原则

基于这次演讲,这里是我们可以立即采取的行动:

1. 采用结构化方法

不要直接让AI生成代码。先研究,再规划,最后实施。

2. 设置人工检查点

在每个阶段结束时验证。研究准确吗?计划合理吗?实施符合规范吗?

3. 压缩上下文

不要把整个代码库扔给AI。提炼出本质,写成清晰的规范。

4. 先手动理解

对于复杂的重构,先手动做一次。赢得理解,再自动化。

5. 保持思考主导

AI是加速机械部分的工具,不是思考的替代品。架构决策必须由人做出。

6. 重视简单性

每次选择"容易"而非"简单"时,问自己:这会增加多少偶然复杂性?

7. 培养模式识别

不要完全依赖AI。通过深度参与代码,积累识别问题的直觉。


尾声

软件危机从未真正消失,它只是换了形式。

从Dijkstra警告的"巨型计算机",到今天的"无限代码生成",本质挑战始终如一:如何在复杂性增长的同时保持理解?

AI给了我们前所未有的生产力,但也带来了前所未有的风险。如果我们不小心,我们将成为第一代被自己的代码库困住的工程师——不是因为代码太少,而是因为代码太多,多到无法理解。

好消息是:解决方案就在我们手中。

不是更好的AI,不是更大的上下文窗口,而是更好的思考方式——一种将AI作为思考的放大器,而非替代品的方式。

当我们学会在生成代码之前先生成理解,我们就能驾驭这个无限的时代。

否则,我们只是在以光速积累技术债。

选择权在我们手中。


如果这篇文章对你有帮助,欢迎点赞、收藏、转发。也欢迎在评论区分享你的经验,我们一起交流学习!


我是 dtsola【IT解决方案架构师 | AI创业者】 ;专注AI创业、商业、技术、心理学、哲学内容分享。

提供服务:AI项目咨询 | 技术解决方案 | IT项目实施 | 企业技术顾问

博客:https://www.dtsola.com

公众号&VX:dtsola

需提供服务,加微信 dtsola,备注:IT咨询,并说明来意。


#独立开发者 #AI编程 #个人开发者 #一人公司 #程序员 #软件开发者 #创业者 #数字游民 #AI创业 #软件工程


Work Less, Earn More, Enjoy Life.