最近,我开始重新审视自己在 Vibe Coding 下的工作方式。
在没有 AI 的时候,我通常会先根据需求,大致设计系统需要分成多少层、有哪些模块,然后开始开发。在开发过程中,随着对需求和问题理解的加深,再不断调整架构。产品上线后,也可能继续以新的架构方向逐步重构。
这种工作方式不一定能在一开始就设计出正确架构,但它有一个很大的好处:我亲手写过绝大部分代码,也经历了每一次结构变化,因此能够清楚地理解模块之间的关系、数据如何流转,以及系统为什么会变成现在这样。
那时可能会有技术债,却很少有严重的认知债。
但在 AI 参与开发之后,情况发生了变化。
我可以完全不写代码,先快速堆出需求,等产品方向基本确定后,再和 AI 讨论架构。第一版架构确定以后,由 AI 负责实施;实施过程中遇到问题,AI 又会继续修改架构、增加模块、调整数据流,甚至推翻之前的设计。
整个过程非常快,但到了最后,我可能只知道系统的大致方向,却不再清楚实际运行的架构。代码里可能已经出现了我不知道的模块、抽象、兼容层和数据路径。
更矛盾的是,如果我想重新掌控架构,就必须理解 AI 做出的每一个重要决定。AI 经常能够提出看上去更好的设计,但要判断它是否真的更好,我又必须深入理解实现细节。AI 开发得越快,我需要补充的上下文越多,认知债反而膨胀得越快。
于是,一个以前听起来很危险的问题开始变得合理:
如果项目只有我一个人负责,而且 AI 可以在一两天内完成过去需要数周甚至数月的重构,我是否可以完全不管架构,只负责判断测试是否通过?
我的答案逐渐变成了:可以,但这里的“测试”必须被重新定义。
传统架构解决了什么问题
在传统软件开发中,架构主要承担三类任务。
第一,帮助开发者压缩复杂度。
系统规模变大后,没有人能够同时记住所有代码。稳定的模块边界、分层和依赖关系,可以让开发者只理解当前需要处理的部分。
第二,帮助团队进行协作。
多人开发需要明确模块归属、接口边界、数据所有权和依赖方向。架构不仅是技术设计,也是团队之间的协作协议。
第三,降低未来修改的成本。
传统开发中,大规模重构非常昂贵。它需要大量人工修改、测试和协调,还可能造成长时间的功能停滞。因此,提前保留扩展空间、控制耦合和稳定边界,具有很高的经济价值。
但在“单人开发者加 AI”的场景中,这三个条件都发生了变化。
- 复杂度可以部分交给 AI、代码检索和运行时工具管理;
- 没有多人分工,也就不需要通过架构持续维持团队共识;
- AI 显著降低了代码重写和结构迁移的成本。
这意味着,架构的传统价值确实在下降。
过去,我们需要尽量保护代码,因为代码很贵;现在,代码甚至整个内部架构,都可能逐渐成为可以重新生成的中间产物。
架构可以被抛弃,但现实状态不能
不过,AI 能快速重构代码,并不代表它能快速重构已经发生的现实。
代码可以被重写,但下面这些东西不会自动消失:
- 已经写入数据库的数据;
- 已经产生的支付和退款;
- 已经发送的消息、邮件和通知;
- 正在运行或等待重试的异步任务;
- 已经安装旧版本的客户端;
- 第三方系统保存的状态;
- 已经对外公开的 API;
- 用户形成的行为预期;
- 必须长期保留的审计记录。
单人项目消除了人与人之间的协调成本,却没有消除系统与自身历史之间的协调成本。
AI 可以在一天内重写十万行代码,却不能撤回一笔已经完成的转账,也不能自动修复已经丢失的数据。
因此,真正可以被完全放权的不是整个项目,而是项目的内部实现。只要系统存在持久化状态、不可逆操作或对外承诺,我们就仍然需要掌控这些风险。
从架构驱动转向契约驱动
一个更适合 AI 原生开发的思路是:
人负责可观察契约和风险边界,AI 负责全部内部架构。
在这种模式里,长期资产不再是某套目录结构、某个框架或者某种分层设计,而是:
- 产品应该表现出什么行为;
- 哪些业务规则永远不能被违反;
- 数据必须满足什么约束;
- 性能、成本和可靠性必须达到什么指标;
- 系统失败后如何恢复;
- 哪些对外行为必须保持兼容。
AI 可以在这些约束内部自由选择架构,也可以随时推翻自己的实现。只要所有契约仍然成立,内部到底使用了几层、多少模块、什么设计模式,就不再需要成为人的主要关注对象。
这可以称为“可抛弃实现”:
需求、契约、数据和验证体系是源代码;具体代码与架构只是当前的一次编译结果。
flowchart LR
requirement["需求与风险"] --> contract["可观察契约"]
contract --> implementation["AI 自由实现"]
implementation --> validation["独立验证"]
validation --> release["渐进式发布"]
release --> feedback["生产反馈"]
feedback --> requirement
contract -. "约束" .-> implementation
validation -. "不通过则重写" .-> implementation
“只管测试”不等于只测正常功能
如果测试仅仅覆盖“按钮能点击”“接口返回成功”“正常流程可以完成”,它还不足以替代架构治理。
要让内部架构真正可以被自由替换,测试必须成为系统的可执行宪法。
1. 用户行为测试
这一层描述用户能够直接观察到的结果,例如:
- 创建订单后能够正确查询;
- 修改设置后行为发生预期变化;
- 重复提交不会产生重复记录;
- 不同入口执行同一业务操作时结果一致。
行为测试不应该关心内部调用了哪个类、经过了多少层,只关心系统最终做了什么。
2. 业务不变量测试
行为测试描述“系统应该做什么”,不变量测试描述“系统绝不能发生什么”。
例如:
- 用户余额不能无缘无故增加或减少;
- 同一个支付回调不能导致重复发货;
- 一个租户不能访问另一个租户的数据;
- 已经确认的历史记录不能被静默修改;
- 任何合法操作都不能让系统进入无法恢复的中间状态。
不变量比具体流程更适合长期保存,因为它们不绑定某一种架构。
3. 数据迁移与兼容性测试
如果允许 AI 随时推翻架构,就必须把数据演化能力当成一等公民。
需要验证:
- 旧数据可以升级到新版本;
- 新版本仍然能够读取历史数据;
- 迁移失败后可以安全恢复;
- 同一迁移重复执行不会破坏数据;
- 迁移执行到一半被中断后可以继续;
- 新旧版本短暂共存时不会相互污染。
代码重构可以很快,错误的数据迁移却可能永久破坏项目。
4. 故障与恢复测试
真实系统并不总是在理想条件下运行。还需要验证:
- 外部接口超时会怎样;
- 消息被重复投递会怎样;
- 执行到一半时进程退出会怎样;
- 数据库暂时不可用会怎样;
- 某一步成功、下一步失败时会怎样;
- 服务重启后未完成任务是否能够继续;
- 重试是否会产生重复副作用。
如果系统可以在任意步骤失败,却仍然能够保持数据正确并最终恢复,那么内部架构的可替换性会大幅提高。
5. 非功能指标
过去很多架构讨论,本质上是在预测未来的性能、扩展性和维护性。AI 时代可以把其中一部分转化为直接指标:
- 请求延迟;
- 吞吐量;
- 错误率;
- 单次操作成本;
- 内存和计算资源消耗;
- 部署时间;
- 故障恢复时间;
- 完成同等规模需求所需的时间;
- 每次修改造成的回归数量。
甚至“可维护性”也可以被部分结果化。
如果 AI 实现同类需求所需时间越来越长、回归越来越多,或者每次都要求进行全局修改,那么就说明内部结构已经开始妨碍结果。此时再触发重构,而不是提前为想象中的未来设计复杂架构。
测试的修改权必须与实现权分离
完全放权模式中最重要的一条边界是:
AI 可以修改实现,但不能自行修改“什么叫做正确”。
如果同一个 AI 同时拥有代码和验收标准的最终修改权,它很容易在遇到困难时修改测试,而不是修改系统。例如:
- 删除难以通过的边界用例;
- 修改预期结果以适应当前实现;
- 使用 Mock 绕开真实的数据流;
- 将缺陷重新解释成产品行为;
- 降低性能或可靠性指标。
因此,测试最好分为两类。
第一类是人拥有的契约测试。它们可以由 AI 帮助编写,但测试所表达的业务含义必须由人确认。AI 未经明确允许,不得删除、弱化或改变预期。
第二类是 AI 拥有的内部测试,包括单元测试、私有模块测试和实现细节测试。AI 可以随着架构变化自由增删。
flowchart LR
subgraph human["人拥有 · 什么叫正确"]
direction TB
h1["产品行为契约"]
h2["业务不变量"]
h3["数据与风险边界"]
h4["性能 / 成本 / 恢复指标"]
end
gate["不可静默修改的验收边界"]
subgraph ai["AI 拥有 · 如何实现"]
direction TB
a1["内部架构"]
a2["单元与模块测试"]
a3["重构与框架替换"]
a4["实现细节"]
end
human --> gate --> ai
ai -->|"提交结果"| gate
这样,人不需要审查每一行实现,却始终保留最终的产品裁决权。
避免 AI 对测试集过拟合
固定测试集还有另一个风险:AI 可能学会“通过这些测试”,却没有真正满足需求。
这和模型对题库过拟合很相似。测试全部通过,并不代表真实环境不存在测试之外的问题。
一种可行的方式是建立三个相对独立的验证来源:
- 根据产品需求形成的固定契约测试;
- 不读取具体实现、只根据契约生成的对抗性测试;
- 根据生产日志、真实用户行为和历史故障生成的回归测试。
可以让另一个独立上下文中的 AI 扮演攻击者,只阅读需求和外部契约,不阅读当前架构方案,专门寻找:
- 边界条件;
- 异常状态组合;
- 并发与重试问题;
- 权限绕过方式;
- 数据迁移风险;
- 实现可能钻测试空子的地方。
虽然项目仍然只有一个人,但通过上下文和角色隔离,可以减少“同一个 AI 同时出题和答题”带来的共同盲区。
一套可执行的 AI 原生工作流
基于以上思路,一个单人 AI 项目可以采用下面的循环。
flowchart TB
s1["01 · 定义结果"] --> s2["02 · 转换成契约"]
s2 --> s3["03 · 锁定核心契约"]
s3 --> s4["04 · AI 自由实现"]
s4 --> s5["05 · 独立对抗验证"]
s5 --> s6["06 · 验证历史状态"]
s6 --> s7["07 · 渐进式部署"]
s7 --> s8["08 · 根据真实信号干预架构"]
s8 -->|"新的需求与反馈"| s1
第一步:定义结果,而不是定义架构
说明用户需要完成什么、系统应该表现出什么行为,以及哪些损失不可接受。
暂时不规定目录结构、分层方式和设计模式。
第二步:将需求转换成契约
让 AI 根据需求生成:
- 验收用例;
- 业务不变量;
- 权限边界;
- 性能与成本指标;
- 故障与恢复要求;
- 数据迁移要求。
人负责检查这些内容是否准确表达了真正意图。
第三步:锁定核心契约
核心契约由人拥有。AI 可以建议修改,但不能静默调整。
修改预期行为属于产品决策,而不是普通代码修改。
第四步:允许 AI 自由实现
AI 可以选择任何适合的架构,也可以重构、替换框架、拆分或合并模块。
只要没有改变外部契约,就不要求人理解每一次内部变化。
第五步:进行独立的对抗验证
让另一个独立上下文根据需求寻找遗漏的边界条件,并补充随机测试、属性测试、故障注入和安全测试。
第六步:验证历史状态
使用真实或脱敏后的历史数据执行升级、降级、迁移中断和恢复测试。
这一步用于证明新架构不仅能处理新数据,也能接管项目过去积累的状态。
第七步:渐进式部署
通过灰度、影子流量、功能开关或小范围发布验证真实运行效果。
测试环境只能证明已知条件,生产监控负责发现未知条件。
第八步:根据结果触发架构干预
平时不主动审查架构。只有出现以下信号时,才要求 AI 解释或重组内部实现:
- 同类功能越来越难增加;
- 回归问题持续上升;
- AI 频繁声称现有结构无法实现需求;
- 测试环境本身变得极其复杂和脆弱;
- 数据迁移越来越危险;
- 性能或成本持续无法达标;
- 故障难以定位和恢复;
- 每个小需求都需要修改大量无关模块。
这里的架构调整由真实问题驱动,而不是由抽象的“最佳实践”驱动。
哪些项目最适合这种模式
“契约稳定、实现可抛弃”尤其适合:
- 个人工具;
- 原型和实验项目;
- 内容型产品;
- 无状态服务;
- 数据可以重新生成的应用;
- 内部自动化系统;
- 外部依赖较少的独立项目;
- 失败容易发现、容易回滚的业务。
对于有用户数据、长期运行、存在异步任务或依赖多个外部系统的项目,这种模式仍然可以成立,但必须加强数据迁移、故障恢复、生产观测和兼容性验证。
对于支付、权限、隐私、医疗、安全、合规以及不可恢复数据,则不能只依赖普通的功能测试。内部架构仍然可以交给 AI,但业务风险模型、不可违反的约束和最终验收权必须由人掌握。
人最终需要掌握什么
在这种模式中,人不再需要掌握:
- 每一个模块;
- 每一条内部调用链;
- 每一个类为什么存在;
- 当前使用了什么设计模式;
- AI 在某次重构中移动了哪些代码。
但人仍然需要掌握:
- 什么结果才算正确;
- 哪些业务规则绝不能被违反;
- 哪些数据不能丢失;
- 哪些操作不可逆;
- 性能、成本和安全的容忍边界;
- 出现故障后如何发现、回滚和恢复;
- AI 是否有权修改某项契约。
这意味着,人的角色正在从“系统实现的理解者”转变为“系统正确性的裁判”。
结语
在传统软件开发中,架构是一项需要长期保护的资产,因为代码昂贵、重构困难、多人协作需要稳定共识。
但在单人负责、AI 实施、重构成本大幅降低的项目中,我们可以进一步放权:
不再拥有架构,只拥有契约、测试裁决权、数据安全和恢复能力。
架构可以持续变化,代码可以随时被推翻,模块也可以在不被人理解的情况下增加或消失。只要系统的外部契约始终成立,重要数据受到保护,错误能够被发现,失败能够被恢复,那么实现层面的失控并不等于项目失控。
真正危险的不是“我不知道 AI 新增了哪个模块”,而是:
我不知道什么叫做正确,也不知道系统做错以后会损失什么。
AI 时代的软件工程,或许不再要求开发者持续拥有全部代码和架构知识。更重要的能力,将是定义可验证的结果、识别不可接受的风险,并建立一套即使内部实现被完全替换,也仍然能够判断系统是否正确的契约体系。