机械臂飞快搭起软件城市的上层功能,一名工程师同时为它补充地基、护栏、检测点与恢复通道

AI Engineering · 2026

AI Coding 时代,
DFX 只会更重要

当功能实现越来越便宜,工程约束反而越来越值钱

14 分钟阅读 发布于 2026 年 6 月 18 日 更新于 2026 年 8 月 9 日

摘要

过去几个月,我越来越多地使用 AI 编写真实的产品代码。我的感受有些复杂:它确实很快、很实用,有时甚至好得出人意料。但“功能做出来了”和“系统设计好了”,从来不是一回事。

问题通常不在于 AI 不会写代码,而在于我们只给了它功能需求,却把可靠性、可维护性、可观测性、可测试性、安全性和可运营性留在了工程师脑中。DFX,也就是 Design for X,正是把这些质量目标重新放回需求和设计。代码生成越便宜,这些约束反而越值钱。

AI Coding 的目标不应该只是更快地产生更多代码,而应该是在更清晰的约束下,产生更好的代码。

第一部分 · 功能能跑,系统呢?

01功能做完了,系统却越来越不敢碰

过去几个月,我越来越多地让 AI 直接写代码。表面上看,它已经非常厉害:让它做一个页面、写一条 API、增加一个定时任务、连接数据库,或者修复一个小 Bug,它通常都能完成得不错。很多时候,结果不只是“勉强能用”,而是真的很快、很实用,偶尔还会让人惊喜。

但在真实产品代码里用了一段时间后,我逐渐注意到另一个问题:AI 很擅长实现功能,却不会自动替我们设计出一个好系统。

一开始,一切看起来都很正常。功能能跑,API 返回了正确数据,前端也接通了,任务执行成功,日志里没有明显报错。随后需求又来了一条,再来一条,然后还有一条。经过几轮修改,代码开始变得异常熟悉:业务逻辑散落在各层,模块边界越来越模糊,相同检查重复出现,状态变化藏在分支里,异常处理各自为政,配置逻辑和执行逻辑纠缠在一起。

换句话说,功能完成了,系统却越来越难碰。

这并不只是“AI 会写烂代码”的问题。人类工程师同样会写出这样的代码。区别在于,AI 可以把这个过程加速很多倍。它能放大好的设计,也能放大缺失的边界、含糊的要求和一次次权宜之计。

2025 年 DORA 关于 AI 辅助软件开发的报告,把 AI 的主要作用概括为“放大器”:它会同时放大一个组织原有的优势和弱点,而不是替代工作背后的整个工程系统。[1] 这份研究讨论的是组织能力,但映射到代码库,我看到的是同样的现象:工程纪律清晰时,AI 会让它跑得更快;设计已经漂移时,AI 只会给漂移增加吞吐量。

真正的问题是,大多数时候,我们只给 AI 功能需求。我们告诉它系统要做什么,却没有明确告诉它:出错时应该怎样表现,未来应该怎样维护,运行状态怎样被看见,新的需求怎样扩展,又该怎样防止人犯下代价高昂的错误。

这就是为什么我认为,在 AI Coding 时代,DFX 只会变得更重要。

02只有功能需求,远远不够

在软件设计中,我们通常会谈到两类需求。

功能需求描述系统“要做什么”:用户登录、创建订单、调度任务、生成报表、处理支付、发送通知或执行交易。这些需求很容易从外部观察。按钮有还是没有,API 能不能调用,流程是完成还是失败,一眼就能看见。

但真实系统的评价标准,从来不只是某个功能能否在理想条件下成功运行一次。系统还需要可靠、可维护、可观测、可测试、安全、可扩展、可运营。我们常把它们统称为“非功能需求”,这个名字听起来像是次要要求,实际上它们对架构的影响,往往比可见功能更深。

Fred Brooks 在《没有银弹》中区分了软件工作的“偶然困难”和“本质困难”:编程语言、工具和表达方式带来的摩擦可以被不断降低,但软件真正困难的部分,仍然是对复杂概念结构的定义、设计与验证。[2] 这篇论文比 AI Coding 早了几十年,把它用在今天是一种推论,但很贴切:AI 能显著降低“把想法写成代码”的成本,却不能替我们决定系统究竟意味着什么、边界在哪里,以及失败之后什么承诺仍然必须成立。

以一个简单的任务执行系统为例。如果只关心功能,实现非常直接:

  1. 从数据库读取任务;
  2. 执行任务;
  3. 更新任务状态;
  4. 返回执行结果。

AI 很快就能写完。但只要把可靠性放进来,设计立刻变得有意思。

如果进程执行到一半崩溃怎么办?同一个任务被两个 Worker 同时领取怎么办?外部 API 已经部分成功,却在返回响应前超时怎么办?业务操作成功后,数据库状态更新失败怎么办?任务该不该重试?操作是否幂等?用户能否安全地再点一次按钮?哪些状态必须持久化,哪些状态可以重新计算?

这些不是无关紧要的实现细节。在生产系统里,真正的事故往往就发生在这些地方。

一个简单的判断标准: 功能验收证明理想路径能跑通;工程验收则要定义,当这条路径被打断、重复、延迟、修改或误操作时,系统还必须守住什么。

第二部分 · DFX 改变的不是名词,而是设计顺序

03DFX 在实践中究竟意味着什么

DFX 是 Design for X,也就是“为了某个目标而设计”。这个 X 可以代表许多不同的质量目标:

面向可靠性和可用性设计:预期失败、限制影响范围,并能在不丢失关键承诺的前提下恢复。
面向可维护性和可扩展性设计:让边界和状态变化足够清楚,能够承受下一次需求修改。
面向可测试性设计:让成功与失败场景中的关键行为可以被控制、观察和验证。
面向可观测性设计:提供能够解释用户影响、当前状态和恢复进度的信号。
面向安全设计:限制权限、校验输入,让危险操作明确可见。
面向运营设计:把发布、回滚、诊断、人工介入和审计做成正常流程。
面向扩展性和成本设计:说清负载假设,以及系统为了履行承诺可以付出多少资源。

下面这张分类图来自更广义的产品设计语境。它把 DFX 分为产品交付、产品演进和产品运行三组,继续向下覆盖采购、供应链、部署、可靠性、可服务性、安全、兼容、复用等目标。[3] 软件团队未必使用完全相同的词汇,但核心思想是一致的:产品必须为功能完成之后的漫长生命周期而设计。

DFX 分类树,将质量目标分为产品交付、产品演进和产品运行三组

图 1 · DFX 贯穿交付、演进与运行。 功能完成只是产品生命周期中的一个瞬间。这张图的意义,在于让质量目标在实现之前就进入设计讨论。

这些话题听起来不如发布一个新功能兴奋,但它们决定了功能上线之后,系统还能不能活下去。一个功能可以在一个下午里写完,一套糟糕的架构却可能惩罚团队两年。

使用 AI 时,这个问题尤其明显。代码生成变便宜了,偶然复杂度也随之变得廉价。我们可以不断要求它:“就加一个判断”“再支持一个场景”“只修这一个 Bug”“先复用现有逻辑”。每一个指令单独看都很合理,整体设计却在持续恶化。

最后,没有人知道真正的业务边界在哪里,没有人知道哪个状态才是权威状态,也没有人知道一次重试是否安全、一次删除能否恢复、一个失败究竟是预期行为、被忽略的问题,还是即将爆炸的风险。

到了这一刻,代码就不再是资产,而开始变成负债。

04AI 为什么容易写出“意大利面代码”

我不认为 AI 写出意大利面代码,是因为它不懂编程。很多时候,它对语法、框架、常见模式,甚至一些架构概念都掌握得不错。问题在于,它默认优化的目标通常太窄。

如果你只说“实现这个功能”,它自然会优化“把当前功能做完”。它会选择一条最直接的路径,让眼前的需求先跑起来。对于一个小脚本或用完即弃的原型,这可能完全合理;但对于需要持续演进的产品,通往今天功能的最短路径,未必是通往可维护系统的最短路径。

代码的恶化通常并不戏剧化:

第一次修改
,为了方便,业务逻辑直接写进 Controller。
第二次修改
,为了省事,几个新条件继续堆进同一个 Service。
第三次修改
,因为改模型看起来太贵,一个新状态被塞进旧字段。
第四次修改
,因为要求只是“先让它跑起来”,异常被捕获后悄悄忽略。

每一个决定单独看都不算灾难,合在一起却形成了一个很难推理的系统。

一个原本整洁的机械装置在一次次合理补丁之后,仍然能够运行,却被杂乱线路和重复控制器完全包围

图 2 · 偶然复杂度,往往就是在“只再改一点”中累积起来的。 绿色指示灯依然亮着,功能也依然能跑。代价只是被转移给了下一个需求、下一次事故,以及那个必须解释哪根线才算权威状态的人。

Martin Fowler 的“设计耐力假说”描述了同一条曲线:忽略设计,早期也许能更快交付功能;但代码库会逐渐恶化,让每一次后续修改都更慢。好的设计让项目能够“更长时间地保持速度”。Fowler 很谨慎地把它称为假说,而不是已经得到客观证明的定律;设计从何时开始产生回报,也取决于具体场景。[4] AI 并没有让这条曲线失效。它只是让更多修改在更短时间内发生,因此我们会更早抵达曲线真正产生后果的部分。

这并不意味着,应该让 AI 在写第一行代码前先造一座宏大的架构。AI 同样会过度设计:层次太多、Helper 太多、配置太多,或者引入一个看起来优雅、产品却根本用不到的抽象。无论来自补丁还是模式,复杂度都不是免费的。

所以,正确的问题不再是“AI 能不能写代码”。它当然能。更好的问题是:在 AI 开始写之前,我们能不能先给它正确的工程约束?

第三部分 · 为失败设计,也为人设计

05可靠性:不要只设计 Happy Path

可靠性是最适合带入 AI Coding 的 DFX 视角之一。可靠的系统不是永远不会失败的系统,那不现实。可靠的系统会预期失败,限制故障影响范围,让失败能够被看见,并给团队留下恢复路径。

最重要的原则很简单:假设失败一定会发生。进程会崩溃,网络会抖动,磁盘会写满,第三方 API 会超时,用户会点错按钮,开发人员也会发布错误配置。一个只有在一切顺利时才能工作的系统,并不可靠,只是幸运。

所以,在让 AI 写代码之前,我更愿意先问下面这些问题:

实现之前的 DFX 问题

  1. 这个流程可能在哪里失败?
  2. 每一种失败发生后,系统会处于什么状态?
  3. 哪些操作必须幂等?
  4. 哪些失败可以重试,重试应该由哪一层负责?
  5. 哪些失败应该告警,哪些必须人工介入?
  6. 为了事后理解事故,需要记录哪些日志和指标?
  7. 为了崩溃后恢复,必须持久化哪些状态?
  8. 用什么证据证明恢复已经完成?

当 AI 先回答完这些问题,后面的实现通常会明显不同。状态变化会更加清晰,重试责任会更加谨慎,重要失败不会被悄悄隐藏,恢复也会从事后补救变成设计的一部分。

前面任务执行器里的超时问题,在 AWS 有一个几乎对应的例子。Malcolm Featonby 描述过一次 EC2 创建请求:服务可能已经创建了资源,但响应在返回途中丢失。如果调用方盲目重试,就可能再创建一台实例。AWS 的做法,是让调用方通过唯一请求标识明确表达意图,使重复请求能够被识别并按幂等语义处理。[5] 这里真正值得借鉴的不是“所有接口都要加一个幂等键”,而是:重试行为应该是 API 契约的一部分,而不是事故之后随手塞进 Catch 里的补丁。

可靠性也不等于平均保护所有路径。Google SRE 把可靠性视为风险管理:服务的可靠性目标应该与业务愿意承担的风险对齐,而不是不计成本追求最高可用率。[6] 这也提醒我们,DFX 不能变成机械清单。营销报表晚十分钟、订单重复扣款和审计记录丢失,显然不应该获得相同的机制和预算。

另一个重要原则是保持简单。一个解决真实问题的简单设计,通常好过一个聪明却制造出五个新问题的设计。但“简单”也不能掩盖单点故障:如果关键流程依赖唯一的服务、唯一的 Job、唯一的配置、唯一的外部系统或唯一的人工步骤,这个风险必须被明确写出来。除非我们要求 AI 从可靠性角度审查设计,否则它未必会主动指出这些问题。

06人为失误,也是系统设计的一部分

很多生产事故,并不是机器自己坏了,而是人在使用系统时,走进了设计没有防护的路径。

有人删错数据,有人发布到错误环境,有人在不了解影响时修改了配置,有人太快地点下危险按钮,也有人因为看不到进度而把同一个操作执行了两次。

责怪用户很容易,但好的系统设计应该主动降低这些错误发生的概率和代价。Don Norman 在《Human Error? No, Bad Design》中说得很直接:如果我们只盯着事故链条最后那个人的动作,就会忽略真正让错误容易发生的设备或流程设计。[7]

映射到软件系统,这个观点会带来非常具体的设计要求:

这些不是功能之外的装饰,而是可靠性和可运营性本身。但很多 AI Coding 提示词只描述用户怎样完成正确操作,很少要求 AI 思考:用户可能怎样做错、怎样失去上下文、怎样重复执行,以及出错后怎样撤销。

第四部分 · 与 AI 建立一份更完整的工程契约

07一种更好的 AI Coding 使用方式

对于任何不只是孤立小任务的功能,我现在都不认为第一步应该是让 AI 立刻写代码。更好的第一步,是先要求它从 DFX 角度分析需求。

例如:

在写代码之前,请从 DFX 角度分析这个需求。
覆盖可靠性、可维护性、可观测性、可测试性、安全性和运营安全。

识别主要失败场景、状态边界、幂等要求、重试行为、恢复策略、
日志与指标、配置校验、人为失误风险,以及未来最可能变化的部分。

然后提出模块结构、实现计划和验证证据。
不要替我猜测业务取舍;把尚未决定的问题明确列出来。

这听起来慢了一步,实际往往更省时间。当 AI 理解了质量目标之后,它就不再只是一个代码生成器,而更像一个工程助手。生成的代码通常更有结构,设计取舍也更容易被评审。

这段提示词不是魔法。你仍然需要质疑 AI 的假设、审查设计、理解自己的系统,并判断哪些风险值得付出成本。真正的变化在于,质量目标不再只存在于评审者脑中,而是正式成为任务的一部分。

为了让实现计划真正可执行,我会要求它至少在六个方面给出证据:

AI 生成变更的 DFX 实用检查
视角 计划必须回答的问题 发布前证据
可靠性 哪里会失败、重复或部分成功? 失败测试、幂等语义、恢复步骤
可维护性 哪个边界拥有这条规则,哪里最可能变化? 模块图、状态模型、明确契约
可观测性 怎样知道用户结果和系统当前状态? 结构化日志、指标、追踪、可行动告警
可测试性 关键状态与依赖能否被控制? 成功、失败和重试的确定性测试
安全与运营 谁可以操作,如何限制影响,怎样撤销? 权限检查、校验、Dry Run、回滚、审计
扩展性与成本 设计基于什么负载和预算假设? 限流、容量测试、降级行为、成本信号

AI Coding 的目标不应该只是更快地产生更多代码,而应该是在更清晰的约束下,产生更好的代码。

08当代码变便宜,架构反而更值钱

有些人认为,随着代码越来越容易生成,架构会变得不再重要。我的判断恰恰相反。

当写代码变得更便宜,决定什么代码值得写就变得更有价值;当实现速度越来越快,错误设计也更容易被放大;当几乎任何人都能生成一个可运行功能,真正拉开差距的,是增加许多功能之后,这个系统是否仍然能够被理解、修改、运营和信任。

AI 可以写代码,但它需要一个清晰的目标。

AI 可以重构代码,但它需要知道重构服务于哪个质量目标。

AI 可以修复 Bug,但它需要理解什么行为才是正确行为。

AI 可以生成抽象,但它并不总能判断这个抽象是否值得它带来的成本。

所以,DFX 并不是一个过时的架构话题。它可能恰恰是我们用好 AI Coding 最实用的方法之一。

未来我们问 AI 的问题,不应该停在:

这个功能应该怎样实现?

还应该继续追问:

这个功能应该在什么质量约束下被实现?

如果跳过第二个问题,AI 的确可以让我们在短期内跑得更快,同时也可能让系统越来越难改、越来越难运维、越来越难信任。代码依然能跑,绿色指示灯也依然亮着,但工程债务可能早已开始复利增长。

参考资料与延伸阅读

  1. DORA. “State of AI-assisted Software Development 2025.” Google Cloud,2025。
  2. Brooks, Frederick P., Jr. “No Silver Bullet: Essence and Accidents of Software Engineering.” University of North Carolina at Chapel Hill,1986。
  3. DFX 分类图原图.” 随 6 月 18 日原文提供的图片。
  4. Fowler, Martin. “Design Stamina Hypothesis.” 2007。
  5. Featonby, Malcolm. “Making Retries Safe with Idempotent APIs.” Amazon Builders’ Library
  6. Alvidrez, Marc. “Embracing Risk.” Site Reliability Engineering,Google,2016。
  7. Norman, Don. “Human Error? No, Bad Design.” 2014。