1. 从 DHH 这句话说起:为什么“手写代码”突然成了一个需要被讨论的话题
DHH 这个名字在 Web 开发圈子里不需要太多介绍。Rails 框架的创造者、Basecamp 的联合创始人、常年以“反主流”姿态出现在技术舆论场里的那个人。他最近抛出的观点是:手写代码的时代正在落幕,Agent 正在重塑软件工程。这句话如果放在五年前,大概会被当成又一个技术布道者的夸张修辞。但放在今天,配合着 Codex CLI、Claude Code、各种 Agent 框架的密集落地,它更像是一个从业者不得不认真对待的信号。
我自己是从 Rails 2.x 时代一路写过来的,经历过“约定优于配置”带来的效率震撼,也经历过前后端分离、容器化、云原生一波又一波的范式迁移。每一次范式迁移都有一个共同特征:它改变的不是某个工具,而是“一个工程师每天到底在做什么”。DHH 这次的判断,本质上也是在说这件事——工程师的时间分配正在发生结构性变化。
这篇文章不打算讨论“Agent 会不会取代程序员”这种已经被嚼烂的话题。我更想从一个实际写代码、带项目、做技术选型的人的角度,把这件事拆开来看:Agent 到底在软件工程的哪些环节已经真正落地了?它的能力边界在哪里?一个普通开发者现在应该怎么调整自己的工作方式?以及,DHH 说的“手写代码时代落幕”,到底落幕的是什么,没落幕的又是什么。
关键词里出现了 Rails、CLI、Agent、软件工程、Codex CLI 这些词,它们其实指向同一个趋势:软件工程的交互界面正在从 IDE 的编辑器窗口,迁移到命令行和 Agent 编排层。这个判断是我写这篇文章的核心出发点。
2. Agent 在软件工程里到底接管了哪些环节
2.1 从“补全”到“执行”:能力层级的本质区别
很多人把 Agent 和早期的代码补全工具混为一谈,这是一个很常见的认知误区。GitHub Copilot 早期做的事情是“预测你下一行要写什么”,它的输出是建议,最终决策权在你手里。而 Agent 做的事情是“接受一个目标,自己规划步骤,自己调用工具,自己验证结果”,它的输出是动作。
这个区别看起来只是程度问题,实际上是层级问题。补全工具需要你坐在编辑器前,一行一行地推进;Agent 可以在你给出一个任务描述之后,自己去读文件、改代码、跑测试、看报错、再改,循环若干轮直到任务完成或者卡住。你从“操作者”变成了“目标定义者”和“结果审核者”。
我在实际项目里用 Codex CLI 和 Claude Code 做过对比测试。给同一个任务:“把这个模块里所有同步的 HTTP 调用改成带超时和重试的异步调用,并补上对应的单元测试”。补全类工具的做法是:我打开文件,它提示我下一行怎么写,我改完一个函数,再打开下一个文件。Agent 类工具的做法是:它自己 grep 出所有相关调用点,自己判断哪些需要改,自己写测试,自己跑一遍看是否通过,失败了再自己修。整个过程我只需要在最后 review diff。
这个差异带来的效率变化不是线性的。改 3 个文件的时候,两者差不多;改 30 个文件的时候,Agent 的优势是数量级的。因为人的注意力切换成本是固定的,而 Agent 没有这个成本。
2.2 已经被 Agent 稳定接管的四类工作
根据我这段时间的观察和实践,Agent 在以下几类工作上已经相当可靠:
第一类是机械性重构。比如批量重命名、提取公共函数、统一日志格式、把某个废弃 API 的调用全部替换掉。这类工作的特点是规则明确、判断点少、验证方式清晰(跑测试就行)。Agent 在这类任务上的成功率很高,因为它的“规划”几乎不需要创造性。
第二类是测试补全。给定一个已有函数,让它生成覆盖边界条件的单元测试。这件事人做起来枯燥,Agent 做起来反而有优势——它不会因为“觉得这个 case 不可能发生”而偷懒跳过。
第三类是文档和注释生成。把一段复杂逻辑翻译成人话,或者根据代码变更自动更新 README。这类工作对准确性要求没那么高,但对“愿意做”的要求很高,正好是 Agent 的舒适区。
第四类是环境搭建和脚本编写。比如“帮我写一个脚本,把这个目录下所有超过 30 天的日志压缩归档,保留最近 7 天的”。这类任务描述清晰、验证直接,Agent 一次成功率很高。
2.3 暂时还接管不了的三类工作
反过来,有几类工作我试下来 Agent 目前还不太行:
架构决策。Agent 可以给你列出三种方案的优缺点,但它没法替你判断“我们这个团队现在的技术债水平,到底该选激进方案还是保守方案”。这个判断依赖对团队、对业务、对历史包袱的理解,这些信息不在代码里。
模糊需求拆解。当需求本身是“让这个页面感觉更快一点”的时候,Agent 会陷入过度工程或者抓不住重点。它需要一个相对明确的目标函数。
跨系统的隐性约束处理。比如“这个改动不能影响 A 服务的缓存命中率”,这种约束往往不在代码注释里,而在某个老员工的脑子里。Agent 看不到这层信息。
把这两组对比放在一起看,DHH 说的“手写代码时代落幕”,更准确的表述应该是:“翻译需求为代码”这个环节正在被接管,而“判断该翻译成什么”这个环节反而变得更重要了。
3. CLI 为什么重新成为 Agent 时代的主战场
3.1 一个反直觉的现象:图形界面输给了命令行
过去十几年,开发工具的主流叙事是“更好的图形界面”。IDE 越来越重,可视化调试、可视化 Git、可视化数据库管理,一切都在往“点点点”的方向走。但 Agent 时代出现了一个明显的反转:最活跃的 Agent 工具几乎都是 CLI 形态。Codex CLI、Claude Code、各种 agent 命令行工具,都是终端里的东西。
这个反转不是偶然的。原因在于 Agent 的工作方式和 GUI 的假设是冲突的。GUI 假设“人来看、人来点”,它的信息密度是为人眼优化的。而 Agent 需要的是可编程的输入输出——它要能读命令的输出、能解析结构化数据、能把结果传给下一个工具。终端天然满足这个要求:文本进、文本出、可以管道、可以脚本化。
我在实际使用中的一个体会是:当我把 Codex CLI 接进我的日常流程之后,最爽的地方不是它多聪明,而是它和现有工具链是无缝的。我可以让它读 git diff,可以让它跑 make test,可以让它把结果写进文件。这些操作在 GUI 里要么做不到,要么需要额外的插件和适配。
3.2 CLI 的“可组合性”才是 Agent 的燃料
终端有一个被低估的特性:一切皆文本,一切可组合。这个特性在 Agent 场景下价值巨大。因为 Agent 的核心能力是“调用工具”,而 CLI 工具天然就是可调用的。
举个具体例子。我让 Agent 做一件事:“找出最近一周改动最频繁的文件,看看它们的测试覆盖率,把覆盖率低于 60% 的列出来”。这个任务需要三个能力:git 历史分析、覆盖率报告读取、结果过滤。在 CLI 环境下,这三个能力分别对应git log、覆盖率工具的输出、grep/awk,Agent 只需要把它们串起来。在 GUI 环境下,这三个能力分散在三个不同的界面里,Agent 根本够不着。
这就是为什么关键词里会出现gitlab cli、codex cli、trae cli这些词。CLI 不是复古,而是 Agent 时代的基础设施。谁的 CLI 工具设计得对 Agent 友好,谁就能被 Agent 生态吸纳。
3.3 一个实操细节:怎么让 CLI 工具对 Agent 更友好
如果你在维护一个 CLI 工具,想让它在 Agent 时代更好用,有几个细节值得注意:
- 输出要结构化。纯文本的表格对人友好,但对 Agent 不友好。提供
--json选项,让 Agent 能直接解析。 - 错误信息要可诊断。不要只输出
Error: failed,要输出Error: failed to connect to database at localhost:5432, connection refused。Agent 需要这些信息来判断下一步。 - 退出码要准确。成功返回 0,失败返回非 0,不同失败类型用不同退出码。Agent 靠退出码判断流程走向。
- 要有 dry-run 模式。Agent 在不确定的时候需要先试跑,dry-run 能大幅降低它闯祸的概率。
这几条看起来是小事,但实测下来,一个对 Agent 友好的 CLI 和一个不友好的 CLI,Agent 完成任务的成功率能差出一倍以上。
4. 一个真实的 Agent 工作流拆解:从任务描述到代码合并
4.1 任务定义阶段:描述质量决定成败
我拿一个真实任务来拆。需求是:“给用户模块加上软删除功能,删除时不物理删除,而是标记 deleted_at,所有查询默认过滤掉已删除记录,并提供一个恢复接口”。
这个描述已经算比较清晰了,但 Agent 拿到之后还是会问几个问题:用哪个 ORM?现有的查询是怎么组织的?恢复接口的权限怎么控制?这些问题的答案不在描述里,而在代码库里。所以 Agent 的第一步不是写代码,而是探索代码库。
这一步的表现是 Agent 能力的分水岭。好的 Agent 会先读模型定义、读现有的查询封装、读路由配置,然后基于这些信息形成方案。差的 Agent 会直接开始改,改到一半发现和现有模式冲突,然后推倒重来。
我的经验是:在任务描述里显式要求 Agent 先探索再动手,能显著提升成功率。比如加一句“先阅读相关模块的现有实现,总结出本项目的数据访问模式,再开始修改”。这句话看起来是废话,但它改变了 Agent 的行为模式。
4.2 执行阶段:Agent 的“思考-行动-观察”循环
Agent 执行任务的过程,本质是一个循环:思考下一步做什么、执行一个动作、观察结果、再思考。这个循环的质量取决于两件事:上下文管理和错误恢复。
上下文管理是指 Agent 能不能在长任务里记住关键信息。一个改 20 个文件的任务,如果 Agent 改到第 15 个文件时忘了第 3 个文件里定的命名规范,结果就是风格不一致。好的 Agent 会维护一个“工作记忆”,把关键决策记下来。
错误恢复是指 Agent 遇到报错时怎么办。我见过两种失败模式:一种是遇到报错就停下来问人,另一种是遇到报错就瞎改,越改越乱。理想的行为是:先读报错、定位原因、判断是环境问题还是代码问题、环境问题就报告、代码问题就修。这个判断能力目前还在进化中。
4.3 验证阶段:Agent 自己跑测试的可靠性
Agent 能不能自己验证结果,是它能不能“闭环”的关键。我实测下来,让 Agent 自己跑测试有几个坑:
- 测试环境不一致。Agent 跑测试的环境可能和 CI 环境不同,导致本地过了 CI 挂了。解决办法是让 Agent 用和 CI 相同的命令跑测试。
- 测试数据污染。Agent 反复跑测试可能留下脏数据,影响后续判断。需要确保测试是幂等的。
- 测试超时。Agent 可能没耐心等一个跑 5 分钟的测试,直接判定失败。需要给它明确的超时配置。
这些坑的共同点是:它们不是 Agent 的智能问题,而是工程问题。把工程问题解决好,Agent 的可靠性会大幅提升。
4.4 审核阶段:人的角色变成了什么
任务完成后,我的工作变成了 review diff。这个环节我现在的做法是:
- 先看 Agent 的“工作总结”,它通常会说明改了哪些文件、为什么这么改。
- 再看 diff 的整体结构,判断改动范围是否合理。
- 重点看它做的“判断性决策”,比如为什么选了这个方案而不是另一个。
- 跑一遍完整测试,确认没有回归。
这个流程下来,我花在“审核”上的时间大概是原来“自己写”时间的三分之一到五分之一。省下来的时间,我用来做那些 Agent 做不了的事:想清楚需求、设计接口、评估技术债。
5. Agent 落地过程中那些没人告诉你的坑
5.1 上下文窗口不是越大越好
很多人以为 Agent 的能力瓶颈是上下文窗口大小,窗口越大越好。实际用下来不是这样。上下文窗口大了之后,Agent 的注意力会被稀释,它可能抓不住最关键的信息。我试过把整个代码库塞给 Agent,结果它反而迷失了,不如只给它相关的几个文件。
正确的做法是精准投喂:让 Agent 自己去找它需要的信息,而不是一次性全给它。这需要 Agent 有好的检索能力,也需要代码库有好的组织结构。
5.2 Agent 的“自信”是个危险信号
Agent 有一个特点:它说话总是很自信,即使它错了。它不会说“我不太确定这个改法对不对”,它会说“我已经完成了修改”。这个特点在审核阶段特别危险,因为人容易被自信的语气影响,放松警惕。
我的应对方法是:对 Agent 的每一个判断性决策都保持怀疑。它说“这里用了策略模式”,我就去看是不是真的适合用策略模式。它说“测试都通过了”,我就自己再跑一遍。这不是不信任,而是这个阶段的必要流程。
5.3 并发场景下的 Agent 协调
关键词里有个词是“ai agent 怎么扛并发”,这是个很实际的问题。当多个 Agent 同时在一个代码库上工作时,冲突是必然的。我的经验是:
- 任务粒度要小。一个 Agent 一个任务,任务之间尽量不重叠。
- 用分支隔离。每个 Agent 在自己的分支上工作,最后合并。
- 合并冲突要人处理。不要让 Agent 自己解决合并冲突,它很容易把别人的改动覆盖掉。
这块目前还没有特别成熟的方案,更多是靠工程纪律来约束。
5.4 安全边界:Agent 能碰什么,不能碰什么
Agent 有执行能力,就意味着它有破坏能力。我给自己定的规矩是:
- 生产环境绝对不让 Agent 直接操作。所有生产变更必须经过人的手。
- 敏感配置不让 Agent 读。API key、数据库密码这类东西,通过环境变量注入,不写进代码库。
- 破坏性操作要二次确认。删除文件、drop 表这类操作,Agent 必须先 dry-run,人确认后才执行。
这些规矩看起来保守,但踩过坑之后就知道,保守是值得的。我见过 Agent 因为理解错了一个路径,把整个测试数据目录删掉的情况。虽然可以恢复,但那个下午的心情是恢复不了的。
6. 软件工程的重心正在往哪里迁移
6.1 从“写代码”到“定义问题”
如果 Agent 接管了“把需求翻译成代码”这个环节,那工程师的核心价值就往上移了一层:定义清楚要解决什么问题。这件事听起来简单,做起来极难。大部分项目的失败不是因为代码写得烂,而是因为解决了一个不该解决的问题。
定义问题需要的能力是:理解业务、识别真正的痛点、判断优先级、设定可验证的成功标准。这些能力在“手写代码”时代被低估了,因为那时候大家的时间都花在实现上。现在实现变便宜了,定义问题的价值就凸显出来了。
6.2 代码审查能力变得比代码编写能力更重要
当 Agent 写的代码比你多的时候,你的核心工作变成了审查。审查能力和编写能力是两种不同的能力。编写能力强的人不一定审查能力强,因为审查需要的是快速识别风险的能力,而不是快速产出的能力。
我观察到的一个现象是:很多资深工程师在 Agent 时代反而更吃香了,因为他们有大量的“模式识别”经验,看一眼 diff 就知道哪里可能有问题。这种经验是 Agent 短期内学不会的。
6.3 对“软件工程”这门学科的影响
关键词里出现了“软件工程导论”“软件工程期末复习”“软件工程毕业设计”这些词,说明这个话题已经渗透到教育层面了。我的看法是:软件工程的基础理论不会过时,但教学重点需要调整。
传统的软件工程教学强调“怎么做”,比如怎么做需求分析、怎么做设计、怎么做测试。Agent 时代需要补充的是“怎么判断做得对不对”。因为“做”这件事正在被外包给 Agent,而“判断”这件事还得人来。
7. 我现在的工作流长什么样
7.1 日常任务的分配原则
我现在会把任务分成三类:
第一类:直接交给 Agent。机械性重构、测试补全、文档生成、脚本编写。这类任务我给出描述,Agent 执行,我审核。
第二类:和 Agent 协作。中等复杂度的功能开发。我先和 Agent 讨论方案,确定后再让它执行,执行过程中我盯着关键节点。
第三类:自己做。架构决策、模糊需求拆解、跨系统协调。这类任务 Agent 帮不上忙,我自己来。
这个分配不是固定的,随着 Agent 能力提升,第一类和第二类的边界在往外扩。
7.2 工具链的实际配置
我目前的配置是:Codex CLI 做主力 Agent,配合 git 做版本控制,配合项目自己的测试命令做验证。关键配置项包括:
- 给 Agent 明确的“项目上下文”文件,说明项目结构、技术栈、代码规范。
- 配置好测试命令,让 Agent 能一键跑测试。
- 设置好权限边界,哪些目录可写、哪些命令可执行。
这套配置搭起来大概花了我半天时间,但后续节省的时间是几十倍。
7.3 一个具体的效率对比
拿一个真实任务对比:给一个中等规模的 Rails 项目加一个“用户活动日志”功能,包括数据模型、API 接口、后台任务、测试。
手写的话,我大概需要 6 到 8 小时。用 Agent 的话,我花 1 小时写清楚需求和技术约束,Agent 执行 20 分钟,我审核和调整 1.5 小时,总共不到 3 小时。效率提升大概 2 到 3 倍。
但这个提升不是无条件的。如果需求描述不清楚,Agent 会跑偏,返工的时间可能比手写还长。所以描述需求的能力成了新的瓶颈。
8. 给不同阶段开发者的建议
8.1 如果你是刚入行的新手
新手最容易犯的错误是“跳过基础,直接用 Agent”。我的建议是:先手写,再让 Agent 写。原因很简单,如果你不知道好的代码长什么样,你就没法判断 Agent 写的代码好不好。
具体做法是:每个任务先自己写一遍,再让 Agent 写一遍,对比两者的差异。这个对比过程是最高效的学习方式。你会看到 Agent 在哪些地方比你强,在哪些地方比你弱,以及为什么。
8.2 如果你是有几年经验的开发者
你的优势是有模式识别能力,劣势是可能对新技术有抵触。我的建议是:把 Agent 当成一个实习生来用。你会怎么带实习生?给他明确的任务、给他足够的上下文、检查他的产出、在关键决策上把关。用同样的方式用 Agent。
不要指望 Agent 一次就做对,也不要因为它做错了就放弃。它的学习曲线(通过你的反馈)比人快得多。
8.3 如果你是团队负责人
你需要考虑的是流程适配。Agent 时代的工作流和传统工作流不一样,代码审查的标准、任务分配的方式、质量把控的节点都需要调整。
我的建议是:先在小范围试点,积累经验,再推广。不要一上来就全面铺开,那样容易出乱子。同时要注意,Agent 的引入会改变团队的能力结构,有些人会适应得很快,有些人会不适应,这个过渡需要管理。
9. 关于“手写代码时代落幕”这句话,我的理解
DHH 这句话如果被理解成“以后不用写代码了”,那是误读。更准确的理解是:“手写代码”作为一种主要工作方式的时代正在落幕,取而代之的是“定义问题 + 审核产出”的工作方式。
代码还是会有人写,但写代码这件事本身的价值在下降。就像计算器的出现没有让数学消失,但让“手算”这件事的价值下降了。工程师的价值正在从“能写出代码”迁移到“能判断什么代码值得写”。
这个迁移对有些人来说是威胁,对有些人来说是机会。威胁在于,如果你过去十年的核心竞争力是“手速快、记得住 API”,那这个优势正在被抹平。机会在于,如果你擅长想清楚问题、擅长判断方案好坏、擅长在模糊中找方向,那你的价值反而被放大了。
我自己是偏乐观的。因为 Agent 再强,它也需要有人告诉它要做什么,需要有人判断它做得对不对。这两个环节目前还离不开人,而且在可预见的未来也离不开。所以与其焦虑“会不会被取代”,不如把精力花在提升这两个环节的能力上。
最后分享一个我自己的小习惯:每次 Agent 完成一个任务,我都会问自己一个问题——“如果这个任务让我手写,我会怎么写?”然后对比 Agent 的写法和我的写法。这个对比不是为了证明谁更强,而是为了保持自己的判断力。因为在这个时代,判断力比产出力更稀缺。