AI 把“做出一个实现”变得很快,但没有让“确定哪个实现值得进入生产”同样变快。很多时候,代码已经不是项目里最慢的部分。真正消耗时间的是确认业务语义、协调上下游、构造可信验证,以及决定出问题时怎样退回去。
这种变化在架构工作里尤其明显。过去,一个代价较高的方案可能停留在白板上;现在,几套都能运行的候选可以在一天内出现。选择变多以后,判断质量反而更重要。错误方向不再因为实现困难而自然淘汰,它也可以迅速长成一套结构完整、测试齐全的系统。
我最近重新整理过一个典型案例。一个内部任务平台最初只有 pending、running、success、failed 四种状态。业务发展后,陆续增加人工审批、暂停、取消、外部系统回调和失败补偿。团队提出“升级成标准状态机”,AI 很快给出了枚举迁移、事件溯源和工作流引擎三套实现,每套都有代码结构、迁移脚本和测试建议。
真正困难的问题没有出现在这些代码里:产品页面中的“已取消”究竟表示用户不再等待,还是所有外部副作用已经停止?一个运行中的任务收到取消请求后,Worker 仍可能成功写回结果,计费和通知应该听谁的?历史报表按旧状态聚合,迁移后如何保持口径?
这些问题不先回答,选哪个框架都只是把含糊语义实现得更彻底。
图 1:判断不是凭资历拍板,而是把事实、约束、备选、验证和复审组织成可以被质疑的过程。
先判断问题属于哪一层
需求常以方案形式出现:“引入状态机”“换成事件驱动”“增加一个 Agent”。资深工程师的第一项工作不是评价技术,而是把方案退回到问题。
在这个案例里,表面症状是状态判断散落在多个服务中。继续追问后,问题分成了三层:
| 层次 | 真实问题 | 不能混在一起解决的原因 |
|---|---|---|
| 业务语义 | 取消、终止、补偿分别意味着什么 | 需要产品、运营和风险负责人共同确认 |
| 领域模型 | 哪些状态与事件合法,谁拥有转换权 | 决定 API、数据库和 Worker 的稳定边界 |
| 基础设施 | 是否需要通用工作流引擎 | 只有规模和能力需求明确后才能评估收益 |
如果直接从第三层开始,平台会得到一个能力很强的引擎,但团队仍然会在每个节点里重复争论“取消后算不算成功”。架构设计不是把复杂度搬进更专业的工具,而是先让复杂度有正确归属。
AI 很适合帮忙列出遗漏场景、检索现有调用方、对比候选方案。它不适合独自定义“已完成”对业务的承诺,因为这个语义来自组织对用户、资金和数据的责任。
用不变量过滤方案,而不是给方案打印象分
我倾向先写一组跨方案都必须成立的不变量,再讨论技术选型:
I1 任务结果只能从一个领域入口确认,API 和 Worker 不得各自写最终状态
I2 取消请求与取消完成是两个事实,不能共用一个布尔值
I3 已发生的外部副作用必须可追踪,不能通过改状态假装撤销
I4 历史报表在迁移期间保持旧口径,并可解释新旧差异
I5 任一阶段都能在 30 分钟内切回旧读取路径有了这些不变量,三个候选的差异就清楚了。简单枚举迁移改动小,但无法自然表达并发事件;事件溯源保留事实完整,却会显著提高查询、运维和团队学习成本;成熟工作流引擎擅长长事务与恢复,但会引入新的运行面和供应商边界。
最终选择未必是最“先进”的方案。对于当时的任务规模,我们采用了显式状态转换表、不可变事件记录和普通关系表投影,没有立刻引入完整事件溯源或外部引擎。原因很务实:它能够解决所有已确认不变量,团队能维护,也保留未来迁移的事件边界。
type RunState = "queued" | "running" | "cancel_requested" | "succeeded" | "failed" | "cancelled";
type RunEvent =
| { type: "started"; attemptId: string }
| { type: "cancel_requested"; actorId: string }
| { type: "effect_committed"; effectId: string }
| { type: "completed"; resultRef: string }
| { type: "cancel_confirmed"; workerId: string };
function transition(state: RunState, event: RunEvent): RunState {
// 转换表由领域规则维护;未知组合必须拒绝,而不是猜一个“合理状态”。
return transitionTable[state]?.[event.type] ?? failInvalidTransition(state, event);
}代码本身并不复杂。关键决定是把“取消意图”“Worker 确认停止”和“外部副作用已经提交”拆成不同事实。这个边界一旦确定,页面、计费、审计和恢复流程才有共同语言。
架构选择要同时计算三种成本
团队讨论方案时,容易只比较开发成本。AI 让开发成本继续下降后,另外两种成本会更加突出。
第一是验证成本。修改一个纯函数可能几分钟就能验证;改变跨服务状态语义,需要历史样本、并发场景、下游消费者和线上对照数据。代码越容易生成,越不能把“实现完成”误当作“语义已经证明”。
第二是认知成本。新增抽象、运行平台或协议都会进入值班和后续需求。一个方案由两个人熟练维护不代表组织已经具备这项能力。设计评审要问:半年后谁能排查它,故障发生时需要跨几支团队,新增成员需要理解多少隐含前提。
第三是退出成本。数据格式、外部协议和用户习惯一旦形成,回退远比删除代码困难。越不可逆的决定,越应该缩小初始承诺、增加对照期,并明确复审触发器。
我会用一张简单的决策表逼迫讨论落到事实:
| 方案 | 首次交付 | 验证难度 | 运行负担 | 退出成本 | 当前判断 |
|---|---|---|---|---|---|
| 扩展枚举 | 低 | 中 | 低 | 中 | 无法表达关键并发事实 |
| 转换表 + 事件记录 | 中 | 中 | 中 | 低 | 满足当前不变量,可渐进演进 |
| 事件溯源 | 高 | 高 | 高 | 高 | 能力过剩,团队准备不足 |
| 外部工作流引擎 | 中 | 高 | 高 | 高 | 等长事务规模达到触发条件再评估 |
这张表不是为了把决定伪装成数学题。它的作用是让反对意见有位置,也让未来复审时知道当初基于什么事实。
让迁移可观察,而不只是可回滚
“保留旧代码”不等于真正可回滚。状态模型迁移涉及持续写入的数据,回退前必须知道新旧语义是否仍然一致。
图 2:新模型先在影子路径中计算,不立即影响用户。差异有分类、有阈值、有负责人,才进入小流量读取。
我们先让旧模型继续作为唯一读写来源,同时根据相同事件计算新状态,但只记录差异。差异不能只统计一个总数,要区分原因:事件顺序问题、历史脏数据、转换表遗漏,还是旧逻辑本身不一致。
migration_gate:
shadow_window: 14d
required_samples: 10000
unexplained_diff_rate: "< 0.05%"
critical_diff: 0
rollback:
switch_read_to: legacy_projection
keep_dual_write: true
owners: [workflow, data, operations]这里的阈值只是结构示例,真实数值应由业务影响决定。权限、资金等场景可能要求关键差异为零;低风险展示字段可以接受短期不一致。重要的是上线前写清楚什么结果允许继续、谁解释异常、多久没有结论就停止扩量。
新模型先服务内部查询,再进入少量租户,最后扩大读取。每一步都保留旧投影和切换入口。删除旧路径则要等完整数据保留周期结束,而不是新版本上线一周后就宣布迁移完成。
判断质量来自校准,不来自职位
架构师也会判断错。真正可靠的做法不是把决定包装得更确定,而是保留当时的假设和复审条件。
我会在 ADR 中记录四类内容:当时确认的事实、仍未知的部分、放弃方案的理由、未来什么信号会推翻当前决定。上线后再回看:差异率是否符合预期,故障恢复是否更快,新增状态的交付时间有没有下降,团队是否真的能独立排查。
有些决定最终有效,只是因为业务规模没有增长;有些方案短期遇到问题,但核心假设仍然正确。只看结果容易把运气当能力。持续对照“当时为什么这样判断”和“后来发生了什么”,经验才会变成可校准的方法。
AI 可以在这个过程中提供很大帮助:扫描调用关系、生成迁移候选、补齐反例、分析差异日志、整理决策记录。它提高的是获取证据和执行验证的速度,而不是替负责人承担结论。
代码产量增加,不代表系统吞吐增加
如果团队每天能生成更多代码,但评审、测试环境、跨团队确认和发布窗口没有变化,整体交付不会按相同比例加速。更常见的情况是,候选实现大量增加,维护者成为新的瓶颈。
因此我不太关心“AI 写了多少代码”或“节省多少编码时间”。更有意义的指标是:从提出问题到获得用户反馈用了多久,重大风险是否更早暴露,变更在评审中往返几次,生产故障能否更快定位和恢复。
成熟团队使用 AI,不是把代码队列堆得更长,而是缩短验证回路:更快做出可丢弃原型,更早生成反例,更便宜地运行迁移演练,更完整地收集上线证据。节省出来的时间应该投入判断,而不是继续扩大改动面积。
工程师的价值也不会收缩成“会不会提示模型”。从用户承诺到数据语义,从接口边界到发布恢复,从技术方案到团队能力,这些仍然需要完整的软件工程经验。AI 让很多实现工作变轻,却让含糊决定更快产生后果。
所谓高级工程能力,不是总能给出更复杂的方案,而是在信息不完整时把问题分层,在多个可行实现中识别真正约束,用可逆方式验证关键假设,并愿意对长期后果负责。代码会越来越便宜,这种判断仍然昂贵。
判断和执行的分离,恰好是 AI 时代人机分工的缩影:模型负责生成候选,人负责分层、约束与复审。候选实现进入生产前的人工确认,在人工审批不是弹窗里被描述成一段可验证的事务协议;而同样的“先定义不变量,再比较实现”思路,在如何让 AI 编程 Agent 在真实仓库里交付里被写成了任务合同。判断稀缺不是新结论,只是 AI 让它在每个环节都更先暴露。