当 Agent 开始写代码,程序员还剩下什么?
过去写一个功能,大致是这样的流程:
理解需求 → 设计数据结构 → 写代码 → 调试 → 联调 → 修 Bug
现在越来越像另一种场景:
描述需求 → Agent 搜索 → Agent 修改十几个文件 → Agent 跑测试 → 功能出现了
更微妙的是,过去很多“懒得补齐”的工程细节 —— DTO、Adapter、migration、SQL、OpenAPI、类型生成、权限校验、测试脚手架 —— 现在 Agent 也可以顺手补上。于是问题不再只是“写代码更快了没有”,而是另一件更根本的事:当 Agent 已经可以承担大量实现工作后,程序员最应该把精力放在哪里?
最开始我给自己的答案也很模糊:架构、设计、审查。
后来我越来越觉得,这些词都太虚了。真正剩下的,其实只有一个更具体的东西:判断。
准确地说,是判断什么值得实现,什么值得成为系统的事实,什么值得被固化为长期约束。后来我才意识到,这些判断最终都指向同一件事:对业务进行正确抽象。
Agent 最危险的地方,不一定是写错代码
我早年接手过一套填报审核系统。现在回头看,这个系统最重要的并不是 Kotlin、Spring Boot、MyBatis 或 MySQL,而是我当时对业务作出的那套抽象。
系统表面看起来很简单:管理员配置活动、公司和表单 → 填报员代表公司填写并提交 → 审核员评分、通过或打回 → 填报员修改后重新提交 → 查分、归档和导出
真正困难的地方,不在 CRUD,而在撤回、审核撤回、打回、重新提交、活动阶段回溯,以及这些行为与角色、公司归属、表单和填报状态之间的关系。
当时客户提出填报需要“打回”,我做了一个看起来很合理的解释:只解锁被打回的行,未被打回的行继续锁定, 以及一个很自然的技术实现:给表的每一行都加上状态。
这个决定此后迅速扩散到整个系统,使得填报状态机迅速膨胀,数十个围绕着填报数据的接口都要考虑各种状态下能够进行的操作,随着开发迭代,这一决策积重难返,成为沉重的技术债务。
这里最值得反思的,不是“我写了一个 Bug”。
恰恰相反,这个系统很多年都可以稳定运行。真正的问题在于:我把“打回的业务意图”过早解释成了“逐行加锁”这个系统事实。
于是我越来越意识到:Agent 最危险的地方,不一定是写错代码;而是它可以极快地把一个错误判断实现得非常完整。
代码越便宜,这个风险反而越大。因为一个不够成熟的判断,不再只是留在脑子里,而是会在几分钟内扩散成数据库、Service、API、前端和测试。
程序员剩下的,首先是决定什么是真的
如果实现越来越容易,那么程序员真正要负责的,到底是什么?
我现在会把它拆成几个问题:
- 系统真正管理的对象是什么?
- 数据属于谁?
- 什么状态是业务事实,什么只是当前 UI 的表现?
- 什么规则无论需求怎么变化都不能违反?
- 哪些字段半年以后很可能完全不同?
- 谁有权解释“最终结果”?
这些问题的共同点是:它们都不是“怎么写代码”的问题,而是“什么应该成为系统事实”的问题。
所以我越来越倾向于用一句更直接的话来理解架构:架构,本质上是在分配刚性。
哪里应该硬,哪里应该软;哪里应该早一点固化,哪里应该继续保持弹性;哪里应该建立唯一事实来源,哪里应该承认自己还没想清楚。
过去我们有时把这种东西叫“品味”。现在我更愿意把它叫作:对系统抽象和变化方向的判断力。
判断一:不要把偶然样本变成永久事实
在我负责的一个产品运营系统中,Excel 导入就是一个很典型的例子。
如果让 Agent 根据业界最佳实践,它会自然而然地这样设计:
Excel 有哪些列 → 数据库建哪些列 → SQL → API → 页面字段
这个过程一点也不荒谬。它甚至很“工程正确”。
问题在于,真实世界里的 Excel 从来没这么听话,而我面临的情况更是如此。客户会改表头,会增加列,不同来源的文件还会携带不同的额外信息。于是你真正要判断的,就不是“该不该建表”,而是:
- 哪些字段代表系统的稳定事实?
- 哪些只是当前某一份输入样本碰巧带进来的信息?
最后我们采取的是另一种方式:
Excel 表头 → 映射少数稳定语义 → 身份、关联、过滤和约束字段进入正式列 → 其余原始字段完整保存到 PostgreSQL JSONB → 同时保留原始文件、Sheet 和来源行号
这里并不是“少做建模”,而是拒绝在证据不足时过早宣布某些字段已经稳定。
稳定事实需要刚性:
- PostgreSQL 约束
- migration
- SQL 查询口径
- sqlc 编译
- 后端事务与计算
- 结果版本和历史快照
频繁变化的外部字段则需要弹性:
- 表头别名映射
- JSONB 原始值
- 导入血缘
- 等查询需求真正稳定后,再提升为正式列
这件事让我越来越确定一句话:正确抽象,并不是消除所有变化,而是把变化留在代价最低的边界内。
判断二:不要为了局部优化制造第二个真相
另一个例子来自产品运营系统的核价功能。
Agent 一度试图在前端实时计算数百条 BOM 的单价。它给出的理由其实也很合理:避免频繁把大量数据发送给后端,减少传输和等待。
如果只盯着“性能”这个局部问题,这个建议并没有明显错误。
但我最后否掉它,不是因为“算不过来”,也不是因为“前端不能算”,而是因为另一个更重要的问题突然变得非常清楚:谁有权定义最终核价结果?
如果答案是后端,那么选价、替代料、汇率、加工费、管理费、覆盖率、阻塞原因和规则版本就必须保持唯一。让前端复制一整套完整计算规则,等于在系统里放入第二个事实解释器:前端规则 ≈ 后端规则
这个等号听起来轻描淡写,实际却是一个需要永久维护的承诺。你必须持续证明它们相等,而架构本身却无法替你保证这件事。
于是这个问题就不再是“能不能优化一次请求”,而是:为了节省一点局部运行成本,值不值得引入长期的一致性成本?
所以我后来越来越相信一句话:不是每个可以优化的问题,都值得被解决。
Agent 特别擅长解决被明确描述的局部问题。程序员真正要保护的,则是那些更重要的不变量 —— 唯一事实来源、单一解释权、错误成本、长期一致性。
但人也不能一开始就知道“正确答案”
如果文章到这里为止,很容易滑向一种过于自信的叙事:好像人天然就知道正确抽象,Agent 只是照着执行,但这远不是 AI 人机协作的答案。
现实复杂的多。
在产品运营系统的核价报价功能里,我自己一开始也不知道正确抽象是什么。
最初真正不确定的,并不是数据库有几张表,而是:
- 用户从哪里进入;
- 谁负责核价,谁负责报价;
- 核价和报价是如何协同工作的;
- 核价所需要的 BOM、单价、汇率、PN 来自哪里;
- 哪些数据允许覆盖,哪些数据是唯一事实;
- 是否应该用一个页面承担所有核价逻辑;
- 报价和核价应该用数据快照还是数据引用;
这些东西如果一开始就靠猜来设计数据库和 API,很容易把尚未理解清楚的流程偶然固化成永久结构。
所以我后来换了一个顺序:先用 Mock 接口把完整流程跑起来,再从流程里提取真正稳定的东西。
可以把这个过程概括成下面这张图,我想保留它,因为它比一堆术语更接近我真实的工作方式:

它想表达的不是“前端先行”,也不是“UI 比数据库更重要”,而是另一件事:复杂业务应该优先在不确定性最大的地方被验证。
更完整一点,就是:Vibe 原型 → 完整走通业务流程 → 暴露状态、异常和数据需求 → 提取业务不变量与变化边界 → 固化数据库、SQL、Service 和 API → 接入真实数据继续校正
这里的 Mock 并不是“临时假后端”,而是一份可执行的业务假设。它的作用不是直接成为最终事实,而是帮助人看清楚什么值得成为事实。
所以,人的价值并不是“一开始就知道正确答案”,而是:知道什么时候还不应该把答案固化。
发现之后,才轮到机器把判断落实
当复杂业务经过流程验证,某些东西开始稳定下来,机器的价值才真正开始放大。
这时候,SQL generation、API generation、类型生成、测试,不再只是“提高效率的工具”,而是在承担一个更重要的职责:把人的判断,变成机器可以拒绝违反的约束。
也因此,我越来越把整个过程理解成三个阶段:探索 → 判断 → 固化
分别对应:Vibe UI → Human judgment → SQL Schema, Query / OpenAPI → Agent implementation
这种理解也改变了我评价技术方案的标准。过去我会更在意一种框架能替开发者少写多少代码;现在我开始更在意,当判断已经形成之后,它能不能被清楚地表达、追踪和验证。
过去,为了让开发者 easy,我们愿意接受很多框架替我们隐藏的复杂度。Spring 做得非常好:写注解、加配置、接依赖,很多能力就自动连起来了。
但 Agent 时代,样板代码已经很便宜。相反,真正昂贵的是那些藏在运行时里的因果链。一个行为为什么发生,最好能够从入口、Service、SQL 一直追踪到数据库,而不是依赖某个容器、某个代理、某组配置在暗中维持语义。

于是我越来越倾向一种简单的目标:
- Developer Easy:让人主要关注高价值判断;
- Agent Simple:让 Agent 面对显式、可搜索、可局部理解和复现的因果链;
- Application Clean:让应用本身保持可靠、可诊断、可应对变化的结构。
这里的 Clean 不是特指某一种分层教义,而是更朴素的意思:一个行为为什么发生,可以沿着最短路径被追踪清楚。
回到标题:当 Agent 开始写代码,程序员还剩下什么?
如果只用一句话来回答,我现在会这样说:程序员剩下的,不是“把代码写出来”,而是决定什么值得被写成系统事实。
更具体一点,程序员真正负责的是这些判断:
- 系统真正解决什么问题;
- 什么必须稳定;
- 什么应该保持弹性;
- 谁拥有事实;
- 哪些业务耦合必须显式保留;
- 哪些错误必须尽早失败;
- 哪些复杂度根本不值得引入;
- 什么时候继续探索,什么时候开始固化。
而 Agent 负责的是另一件事,把这些判断:
- 变成 migration
- 变成 SQL
- 变成 generated queries
- 变成 Service
- 变成 API adapter
- 变成前端类型
- 变成 UI
- 变成 tests
这也是为什么我越来越觉得,Agent 时代真正稀缺的不是写代码的速度,而是另一种能力:判断在哪里探索,在哪里保持弹性,在哪里建立唯一事实,以及什么时候应该开始固化。
对简单 CRUD,可以从稳定数据向外展开。
对复杂业务,先让用户完整走一遍流程,再从行为中提取状态、数据和边界。
用 SQL 固化内部事实,用 OpenAPI 固化外部契约,用显式装配保持可靠因果链,用 Agent 承担大规模的确定性实现。
Agent 让代码越来越便宜,但它没有让判断变便宜。恰恰相反,当一个判断可以在几分钟内被扩散成数据库、Service、API、前端和测试时,判断本身比过去更昂贵了。
业务抽象由人判断,事实与契约由机器强制,连接与实现由 Agent 承担。