DHH 最近一句话,几乎把软件工程圈聊炸了:“手写代码时代落幕,Agent 正重塑软件工程。”很多人第一反应是:这老头又在哗众取宠吧?毕竟他一手搞出 Ruby on Rails,又带着 Basecamp 高调喊“远程办公”,向来是技术圈最敢说的意见领袖。但你真把他在分享里讲的内容听完,会发现这不是末日宣言,而是一次对工作方式的重新定义——他说的是软件开发的入口正在从“人肉敲键盘”变成“人与 Agent 协作的工程设计”。
这篇文章我想结合 DHH 的原话,以及我自己这一年多折腾 AI Agent、做软件工程项目的真实体会,把“手写代码落幕”这件事拆开揉碎讲清楚:Agent 到底改变了什么、没改变什么,以及普通开发者、软件工程专业学生现在该怎么接住这波变化。不管你写前端、写后端,还是正在为软件工程导论期末复习头疼,这篇都值得花十分钟读完。
1. DHH 是谁,他的发言为什么值得认真听
1.1 从 Rails 到 Basecamp:一个“反主流”技术领袖的发言分量
先给不熟悉的朋友补个背景。DHH 全名 David Heinemeier Hansson,丹麦程序员,2004 年发布 Ruby on Rails 框架,算是 Web 开发史上绕不开的人物。Rails 用“约定优于配置”的理念,把 MVC 架构推向主流,让一代人重新理解了什么叫高效开发。之后他创办 37signals(后来改名 Basecamp),用一个小团队做出全球众多公司依赖的协作工具,还写了《Rework》和《Remote》,持续输出“小团队、高产出、少开会、少加班”的极端务实主张。
为什么他的言论值得听?因为这个人有完整的产品、公司和行业战绩背书,而且他不靠卖课、不靠流量吃饭。他做 HEY 邮件服务、做 ONCE 系列软件,是真的在写生产代码的人。一个真正长期在一线写代码、带产品的人,站出来说“手写代码时代落幕”,分量和自媒体蹭热度完全不同。更何况,他是 Rails 的作者——Rails 本身就是为了“减少重复劳动”而生的框架级产物,他对“提高开发效率”这件事的发言权,圈内没几个人能比。
1.2 “手写代码时代落幕”到底指什么,不是程序员末日
先把定心丸吃掉:DHH 没有说程序员明天就失业。他在表达里反复强调的,是软件工程的生产方式正在发生质变。以前我们写一个功能,要经历“拆需求、设计接口、写代码、联调、测试、部署”这一整套流程,其中“写代码”占用了开发者的绝大部分精力。而现在的 AI Agent 可以承担部分代码实现、测试执行、错误修复、文档生成这类“执行性工作”,开发者的角色开始向“定义问题、拆解任务、审查结果、保障质量”上移。
他用了一个很形象的类比:过去你是那个亲自搬砖砌墙的泥瓦匠,现在你更像工头,负责看图纸、分活、验工,砖块由机器和工人替你搬。所谓“手写代码时代落幕”,落幕的不是“写代码”这个动作的结果,而是“把写代码作为核心生产瓶颈”的旧模式。代码从来都不是软件的目的,软件是为了解决业务问题而存在的,现在 Agent 能把“问题到代码”的距离压缩到极短,这确实是一个历史性节点。
1.3 DHH 为什么偏偏强调 Agent,而不是普通 AI 补全工具
这里有个关键区分:Copilot 那种“你写一半,它帮你补另一半”的交互,本质上是“更好的自动补全”。它仍然需要开发者一句话一句话地引导,每一段代码的语义决策还是人在做。而 Agent 是“你交代一个目标,它自己规划步骤、调用工具、验证结果、修正错误”,执行单位从“代码行”变成了“任务”。
DHH 在公开发言里反复提到,他已经被这种“任务级代理”的效率震撼到了:给 Agent 一个清晰的规格说明,它能把整个模块的骨架搭出来,自己跑测试、读报错、修 bug,循环迭代。这不是把键盘前的程序员替换掉,而是把原本属于程序员的“累活”压缩成“审活”。Agent 正重塑软件工程,核心就在这个词——“重塑”,而不是“替代”。
2. Agent 重塑软件工程的底层逻辑
2.1 从“AI 补全代码”到“AI 智能体”:差在哪里
两年前大家聊 AI 编程,默认指 GitHub Copilot 这类行级补全。说实话,这玩意儿解决的是“打字速度”问题,对我的帮助更多是少敲几个括号、少拼几个变量名。它的上下文基本局限在当前文件,跨文件、跨服务、跨系统的理解能力非常弱,本质上还是个超强输入法。
Agent 则完全不同。以一个典型的代码生成 Agent 为例:你给它一个需求“把订单服务重构为异步消息处理”,它会自己拆出“读现有代码结构、梳理依赖关系、设计新接口、生成迁移代码、写单元测试、跑测试、根据报错修改”这一串子任务,然后循环执行,直到满足验收标准。这个过程里,它的上下文管理、工具调用、结果反思都是自主完成的。我实测下来,最直观的体感区别是:Copilot 像“实习生帮你补全句子”,Agent 像“一个能远程结对、会自己查资料、会自己跑测试的初级工程师”。
2.2 Agent 的三大核心能力:任务拆解、工具调用与记忆管理
拆开看,Agent 能承担工程任务,靠的是三件事:
第一是任务拆解(Planning)。大语言模型本身有很强的自然语言理解能力,但要把一个含糊的需求变成可执行的多步计划,需要 Agent 框架层面的规划和分解机制。比如 ReAct 模式(Reasoning + Acting)让它能“想一步、做一步、看结果、再想下一步”,这个“反馈闭环”是 Agent 比固定提示词脚本强大的根本原因。
第二是工具调用(Function Calling)。Agent 不能只动嘴,它需要真正操作代码仓库、执行命令、调用 API、读写数据库。现在主流模型都支持结构化工具调用,Agent 可以自主决定“下一步该调用哪个工具、传入什么参数、怎么处理返回结果”。我经常跟朋友说,没有工具调用的 Agent 只是个聊天机器人,有了工具调用它才成为“代理”。
第三是记忆管理(Memory)。短任务靠上下文窗口就够,长任务必须把关键信息沉淀下来。现在成熟的方案是把里程碑产出、决策记录、错误日志写成结构化笔记,存进长期记忆,让 Agent 在后续步骤里可以随时检索。这跟人写开发文档是一个道理——你不是把所有代码记在脑子里,而是知道去哪查。
2.3 软件工程的瓶颈正在上移
为什么说“手写代码时代落幕”?因为当代码生成变得廉价和规模化,软件开发的核心瓶颈会从“写不写得出来”变成“定义得清不清楚、审得严不严格”。再往前推一步:过去一个团队最大的成本是开发工时,以后最大的成本是需求澄清、架构取舍、质量保障和系统维护。换句话说,理解业务的人价值更高了,只会照需求敲代码但不懂为什么的岗位会快速被压缩。
这个变化对软件工程专业学生的学习路径影响尤其大。我见过不少在读学生还在死磕“软件工程导论第六版课后答案”,把精力全花在背概念上。但行业已经变了:概念当然要懂,可更重要的能力变成“能不能把模糊需求结构化”“能不能设计出 Agent 可执行的规格”“能不能审查 Agent 生成代码的质量”。这些能力不是靠背答案能获得的,必须靠真实项目练。
3. Agent 在软件工程中的落地场景
3.1 从需求描述到代码实现:从“写代码”变成“提需求”
我今年配合 Agent 完成了好几个从零到一的功能模块,最典型的是一个内部数据看板。以前这种活,我得先搭前端框架,再写后端聚合接口,再写可视化组件,前后三四天。这次我给 Agent 一个非常具体的需求描述:“读取 Postgres 里的订单表,按日聚合销售额和订单量,生成一个带趋势折线的 Web 看板,技术栈用 React + Express,跑在 Docker 里。”它自己完成了项目初始化、依赖安装、API 编写、前端页面、Dockerfile 编写,我只需要在它跑偏时纠正方向、在关键节点审查代码质量。
这里要强调,Agent 最怕“模糊指令”。你给它“帮我做个看板”,它只能猜;你给它“字段、口径、技术栈、部署方式、验收标准”,它就能输出接近可用的结果。所以“提需求”的能力正在变成新的核心竞争力,需求描述得越清晰,Agent 的质量上限越高。我的经验是,写需求描述时至少包含六要素:输入是什么、输出是什么、核心业务规则、技术约束、验收标准、非目标(明确不要做什么)。
3.2 自动化测试、Code Review 与缺陷修复:Agent 的质量岗
代码生成只是一半,真正费精力的是质量保障。我现在大量使用 Agent 做测试用例生成:把一段功能代码丢给它,让它补齐单元测试、边界测试和异常路径测试。它不只会写测试,还能自己跑测试、分析覆盖率、根据失败信息改代码。上周一个老项目的支付模块重构,Agent 帮我补了 60 多个测试用例,我发现漏考虑的三个边界条件,都是它先生成测试再去验证代码覆盖时暴露出来的。
Code Review 场景同样很香。Agent 可以把 PR 里每个文件的改动、潜在 bug、风格问题、安全隐患都扫一遍,输出带行号、带严重级别的审查意见。我个人的实践是:先让 Agent 做第一轮机械性审查,再让人做第二轮语义性审查,效率至少提升一倍。有一点必须提醒:Agent 的审查意见偶尔会“误报”,它会因为训练数据的偏差而给出并不适用于当前架构的建议,所以不管 Agent 说什么,最终拍板的一定要是人。
3.3 运维、部署与监控中的 Agent:DevOps 的自动化升级
软件工程的另一半是“保障运行”。Agent 在这个场景里同样凶猛:诊断告警、定位日志、回滚版本、扩容实例、执行运维脚本,这些都是高重复、强流程、可验证的任务,非常适合 Agent 化。我再举一个真实例子:之前线上有个服务内存不断上涨,我把告警信息、最近变更列表、监控面板截图一起丢给 Agent,它先是梳理出可能是缓存未失效导致的嫌疑,然后写了个临时脚本去统计 key 的增长速率,最后给出了两条修复建议,还自动把补丁代码发成了 PR。整个排查过程,放在以前我至少要花半天。
这个方向也正在催生大量新岗位,网上到处是“Agent 部署测试软件”“AI Agent 怎么扛并发”这种热词。扛并发这个点尤其关键,因为 Agent 不是单机玩具,放到生产环境要面对海量请求。现在主流的做法是:把 Agent 做成无状态服务,外面挂任务队列,用异步 worker 去消费,配合限流、幂等、超时重试和横向扩容。记住一句话——Agent 服务本身很脆,但任务调度层可以做得很稳。
3.4 文档生成、重构与遗留系统维护:沉默的增量
还有一个容易被忽视的场景:文档与重构。传统项目最烦人的是 README 过期、接口文档缺失、老代码没人敢动。Agent 可以把陈旧代码库扫描一遍,生成结构说明、函数索引和模块依赖图,还能生成迁移建议。我上个月整理一个五年没人碰过的老系统,Agent 帮我把 200 多个老旧 Python 文件梳理成了清晰的模块地图,还自动生成了技术债务清单,这份材料直接成了技术部改造立项的依据。
对软件工程课程设计和毕业设计来说,这个能力简直是外挂。很多学生项目不是技术难,而是“写了代码但没文档、没测试、没部署说明”,用 Agent 可以把这些工程化基础设施快速补齐,让课程设计从“能跑”变成“像一个正经软件工程项目的产物”。但我也要说句实在话:用 Agent 补齐工程化是可以的,用 Agent 把课设代码全自动写完然后一问三不知,那是害自己。
4. 从 0 到 1 搭建一个属于自己的 Agent
4.1 主流 Agent 框架盘点与选型
如果你想自己动手搭建 Agent,现在框架已经非常丰富了。我用过其中好几个,简单说说各自定位,方便你选型。
| 框架 | 定位 | 适合场景 | 上手难度 |
|---|---|---|---|
| LangGraph | 图状态工作流 | 复杂多步骤、需要精细控制的工程任务 | 中等 |
| AutoGen | 多智能体对话 | 多角色协作、争论式讨论、复杂推理 | 中等 |
| CrewAI | 角色化协作 | 轻量快速搭建“项目经理+开发+测试”团队 | 低 |
| Dify | 可视化应用平台 | 快速做应用编排、知识库 RAG、服务发布 | 低 |
| Spring AI / ADK | 企业级生态接入 | Java 栈团队、需要和 Spring 体系深度整合 | 中等 |
| OpenAI Agents SDK | 轻量工具调用框架 | 已有大模型 API,想快速跑通 Agent 循环 | 低 |
给个选型建议:个人实验和课程设计,直接选 CrewAI 或 OpenAI Agents SDK,十来行代码就能跑通一个 Agent 循环;要做精细工程化流程,LangGraph 值得投入时间学;要在企业 Java 环境落地,可以重点看 Spring AI Alibaba 和 Google ADK,前者对国内云生态友好,后者对多模型编排支持好。
4.2 最小可用的代码生成 Agent:架构与步骤
我写一个最简单、可复现的“最小 Agent”思路,你照着搭就能理解 Agent 的工作机制。核心是三步:定义工具函数、编写 Agent 循环、绑定大模型。
先看工具函数。Agent 得能“动手”,所以我们要给它两个工具:执行 Shell 命令的run_command和读写文件的read_file/write_file。下面这是用 Python 写的一个极简版:
# 工具层 def run_command(command: str) -> str: """执行一条 Shell 命令,返回输出。""" import subprocess result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30) return result.stdout + result.stderr def read_file(path: str) -> str: """读取指定文件内容。""" with open(path, 'r', encoding='utf-8') as f: return f.read() def write_file(path: str, content: str) -> str: """写入文件内容。""" with open(path, 'w', encoding='utf-8') as f: f.write(content) return f"文件 {path} 写入成功"然后就是 Agent 循环。为了不过度依赖某个框架,这里用最朴素的对话式循环演示:每次让模型根据工具返回结果决定下一步做什么,直到它认为任务完成。核心逻辑是:把工具列表传给模型,模型输出“我现在要调用某工具,参数是什么”,系统执行后把结果反馈给它,如此反复。
# Agent 循环主逻辑 tools = [ {"name": "run_command", "params": {"command": "str"}, "func": run_command}, {"name": "read_file", "params": {"path": "str"}, "func": read_file}, {"name": "write_file", "params": {"path": "str", "content": "str"}, "func": write_file}, ] def run_agent(user_goal: str, max_iter: int = 20): history = [{"role": "user", "content": user_goal}] for step in range(max_iter): # 把工具描述注入系统提示,让模型知道可用什么 tool_desc = "\n".join(f"- {t['name']}: {t['params']}" for t in tools) prompt = "你是代码生成 Agent。可用工具:\n" + tool_desc + "\n请选择下一步动作。" messages = [{"role": "system", "content": prompt}] + history # 请求大模型(此处为伪代码,按具体 API 实现) response = call_llm(messages) history.append({"role": "assistant", "content": response}) if "任务完成" in response: break # 解析模型要调用的工具,执行并把结果写回 tool_call = parse_tool_call(response) # 伪代码 if tool_call: result = execute_tool(tool_call, tools) history.append({"role": "user", "content": f"工具结果:{result}"}) return history这个循环虽然简陋,但已经把 Agent 最核心的“模型决策-工具执行-结果反馈”结构跑通了。运行时你要注意:给模型提供清晰的工具描述,比什么都重要;工具返回的结果要尽量结构化,报错信息要保留原始输出,这样模型才能自我纠错。
4.3 Agent 记忆与工具:容易被忽略但决定上限的两件事
很多人搭完上面的最小 Agent,兴奋劲过去后发现它“不聪明”:多轮修改后忘记最初要求、同一个问题反复问、长任务做着做着就跑偏。这基本都是记忆设计不到位。
短期记忆就是上下文窗口内的对话历史;长期记忆则要借助外部存储,比如向量数据库。标准做法是让 Agent 在完成每个子任务后,把“完成内容、关键决策、待办事项、踩坑记录”写入记忆文件,下次需要时再读出来。我常用的是memory.md文件方案:每次 Agent 行动前,先读这个文件,行动后更新它。别小看这个朴素的方案,它能解决大部分长任务漂移问题。
工具设计也有讲究。别一味追求给 Agent 越多工具越好,工具越杂,模型选择错误的概率越高。我建议采用“小而精”的原则:每个工具只做一件事,命名要直白,参数要有默认值,函数内部要有超时和异常兜底。对代码生成类 Agent,最好把“执行命令”和“读取报错”设计成两个独立工具,因为模型经常需要专门看一眼报错内容来决定下一步。
4.4 AI Agent 怎么扛并发:从玩具到生产环境
网上有个热词叫“AI Agent 怎么扛并发”,这确实是从个人玩具走向团队工具的一道坎。我之前把一个 Agent 服务放到公司内部用,第一次上线就被流量打崩了——模型 API 限流、数据库连接池耗尽、任务重复执行,全部踩了一遍。
现在稳定运行的方案是这套组合拳:
- 客户端请求先进网关,网关做身份认证和限流,防止单个用户把 Agent 资源吃光。
- 把任务放进消息队列(Redis Stream 或 RabbitMQ),worker 异步消费,天然支持横向扩容。
- 给每个任务生成唯一 ID,消费时做幂等校验,防止 Agent 重试造成重复写库。
- Agent 内部给每步工具调用设超时和重试上限,避免一个坏工具拖死整个流程。
- 大模型 API 做多模型容灾,主模型挂了自动切备用模型,只损失少量质量但保证服务不断。
把 Agent 部署成无状态服务是核心竞争力。所有中间状态都要放到外部存储,这样任何一台 worker 挂了,另一个 worker 可以接着任务 ID 继续执行。我在生产环境跑过的最长任务是一个全量代码库扫描,耗时 40 分钟,中间 worker 重启了两次,靠状态外部化硬是没丢进度。
5. Agent 落地的常见问题与避坑实录
5.1 “Agent execution terminated due to error”:执行失败的排查套路
很多新手第一次跑 Agent 框架,会看到形如“agent execution terminated due to error”的报错,瞬间不知道从哪下手。我总结了一套排查顺序:
- 先看是不是工具执行超时或崩溃。很多 Agent 框架默认工具超时是 30 秒,跑编译、跑测试很容易超时,把超时调大就行。
- 再看是不是上下文爆了。任务步骤太多、工具返回结果太长,会把上下文窗口塞满,模型开始胡言乱语甚至直接终止。解决方案是给工具响应做截断摘要、把历史记录压缩。
- 然后看是不是沙盒环境问题。我之前遇到“显示更新 agent 沙盒”的提示,其实是执行环境里的依赖没装全,Agent 每次跑命令都报 module not found,循环几次后主动终止。这时候应该把环境初始化脚本放进系统提示词,让 Agent 第一步先准备环境。
碰到任何执行错误,第一件事永远是打开日志,把最后一个动作和对应工具结果完整看一遍。Agent 的终止永远有原因,而原因通常藏在最后一步。
5.2 上下文爆掉与结果漂移:长任务的两个隐形杀手
短任务里 Agent 表现出色,一到长任务就翻车,这是最常见的痛点。上下文爆掉我上面说了,结果漂移则更隐蔽:Agent 跑着跑着,忘了最初的需求约束,产出了和原始目标不一致的东西。比如你要求“保持现有 API 兼容”,它做到第 8 步时擅自改了接口签名,还觉得“这样更合理”。
对付结果漂移,最有效的办法是给 Agent 一个“任务契约”:把所有不可变约束写进一个CONTRACT.md文件,在系统提示词里让它每完成一个阶段就对照契约检查一次。我在实际项目里就靠这一招,把 Agent 的返工率降了至少一半。另外一个土办法是定期让 Agent 输出“当前进度简报”,人来审一眼,跑偏了马上拽回来。
5.3 Agent 安全:权限、幻觉与数据泄露
Agent 能调用工具后,安全边界就是一个必须正面谈的问题。我最基本的原则是:Agent 永远跑在沙盒环境里,不能直接碰生产数据;所有高危操作(删表、改权限、发消息、上生产)必须人审确认;对第三方 API 的调用要单独做白名单和密钥隔离。
幻觉问题也要有预期。Agent 在代码生成时经常一本正经地编造不存在的 API、虚构函数签名,或者把两个库的用法混在一起。这不是靠提示词能根治的,只能靠验证:所有 Agent 生成的关键代码,必须经过编译、测试、人工审三道关,缺一不可。我见过最快的翻车方式,就是团队把 Agent 的输出当成“权威结果”直接合入主干。
在团队里,我还强烈建议给 Agent 一个独立账号,最小权限原则,所有操作留痕。它做的一切都要能回溯,不然出问题时无从查起。
5.4 团队落地 Agent 的几条实战建议
如果你打算在团队里推 Agent 落地,我的建议是不要一上来就追求“全流程自动化”。先从三个点切入最容易见效:写单元测试、做代码审查、写技术文档。这三个场景风险低、反馈快、人工兜底容易。跑通之后再考虑“需求到代码”这种更大范围的使用。
另外,团队推行 Agent 最怕的是“会的人很爽,不会的人很慌”。我建议固定时间段做内部学习分享,或者先拉起一个小的 Agent 兴趣小组,让几个同学把典型场景的实用配置沉淀成团队模板,其他人直接用模板。这里可以参照现在很火的“Agent 开发学习路线”:先学会调用现成框架,再理解记忆与工具设计,然后学多 Agent 协作,最后做生产化部署和评估。路线很清楚,关键是要真的花时间去跑几个项目,只看教程是学不会的。
6. 再谈 DHH 的“落幕论”:我的真实看法与实践感受
把话题拉回 DHH。我认为他这句“手写代码时代落幕”的准确含义是:手写代码不再是工程师最稀缺、最有价值的产出。当代码生成从“需要人一行行敲”变成“人与 Agent 协作生成”,工程师的核心竞争力就不再是打字速度,而是问题定义能力、架构判断能力、代码审查能力和系统思维。你依然要会写代码——不会写代码的人没法审查 Agent 的产出、没法设计可执行的规格,但你不需要把 100% 的精力花在实现上。
我自己这一年多的体感是:同样的功能量,我花在“写”上的时间可能少了 60%,但花在“定义需求、设计架构、审查质量”上的时间增加了。项目整体周期没有缩短好几个数量级那么夸张,但团队可以同时推进的任务量明显变大了,孤胆英雄式的全栈手写越来越没意义。
最后分享一个我现在工作里的固定习惯:每天早上上班,先花 15 分钟把当天所有可委派的开发任务写成 Agent 能理解的规格说明,然后让 Agent 去并行跑,我隔一段时间去看一轮产出。这个习惯坚持了一个季度之后,我最大的体会不是“我变闲了”,而是“我终于有时间去思考原本没时间想的问题”——比如系统架构的演进化方向、业务模型的抽象、团队协作流程的改良。这才是“手写代码时代落幕”之后,一个软件工程师真正该花时间的地方。