用AI写代码的人大概率都经历过这种时刻需求说得差不多了AI几秒钟吐出一大段能编译的代码那一刻确实爽。但用久了你会发现真正耗时间的根本不是“让AI把功能写出来”而是它交完代码之后你还要做的那些事——审查、补依赖、修边界、跑测试、理提交。这个“最后一步”才是长期用AI编程最大的心结。这篇文章不聊怎么让AI写更多代码我想认真聊聊这个“最后一步”它为什么这么烦根子到底在哪以及我踩了无数次坑之后沉淀下来的一套收尾工作流。如果你也用AI写代码而且总感觉“生成一时爽合入火葬场”那这篇应该能帮到你。1. 卡住我的不是写代码是写完之后的那个时刻1.1 一次本应很爽的AI编程最后难堪收场我印象很深的一次经历是给某个内部工具写一个日志导出功能。需求很清晰筛出指定时间范围内的日志按级别分组导出成CSV。我打开AI对话窗口把需求一贴它很快就给出一版能跑的Python脚本。那一刻我觉得今天能提前下班。结果并没有。把代码贴进项目里一跑先报缺依赖装上依赖后发现它自己写了一个工具函数可项目里早就有现成的功能还重叠再往下走时间字段是字符串它没处理空值直接崩溃最后想提交发现函数命名风格跟整个项目不一致更别提完全没有测试。等我把这一摊子收拾完两个小时已经没了。AI生成这段代码只花了大概五分钟。后来我复盘了一下这种“生成五分钟收尾两小时”的戏码几乎每周都在上演。问题根本不在AI写得差而在我们忽略了“收尾”这件事本身的复杂程度。1.2 “最后一步”不是一个动作而是一整套工程动作很多人提到收尾第一反应是“提交代码”。但真实项目里的收尾是一整套动作的组合审查AI生成的代码是否合理跟现有架构是否一致把所有新增的依赖、工具函数、资源文件归位处理没考虑到的边界条件、异常分支补测试至少保证不会破坏已有功能跑完整的构建与检查流程解决风格、类型、接口问题把一坨改动切分成有意义的提交写清楚变更说明手写这些代码的时候这些动作很多是自然发生的——你有上下文代码是你自己的思路写的过程里已经顺手把边界和风格都考虑进去了。但AI生成不一样它给你的是“成品”而且是一个假装完整的成品。它不会告诉你哪个地方要改、为什么这么写、和现有代码有什么关系。于是所有本该分布在整个开发过程中的琐碎决策全部被压缩到了最后一步。所以才会那么烦。1.3 90%陷阱AI让我们更快到终点前也让我们更不想跑最后一段我把这个现象叫作“90%陷阱”。手写代码时前90%的进度其实很慢因为你在同时做“写”和“想”但最后10%很顺因为代码是你自己的你心里清楚每一步该做什么。AI反过来它把前90%变得极快快到你觉得已经完成了可最后那10%——审查、修改、验证、合入——因为缺少上下文反而变得比手写时更慢。更糟的是这最后一段的“慢”是感知不到的。人很容易高估“代码能跑”和“代码可交付”之间的距离于是每次都在收尾时被打个措手不及。我后来才慢慢想明白要解决这个问题不是让自己更细心而是要把收尾这件事本身工程化。2. AI生成代码总差一口气三个根源我踩了无数次才想明白2.1 上下文盲区AI看不到你的仓库里已经有什么AI生成的代码差一口气第一个根源是上下文盲区。它不知道你的项目里已经有哪些工具函数、类型定义、接口约定、目录规范。你给它一段需求它只能基于训练时见过的“通用写法”来发挥。典型场景项目里明明有一个统一的日期格式化工具AI会自己再写一遍项目里所有文件都有header注释模板AI生成的代码光秃秃项目用类型别名管理状态AI直接给你塞了一个裸对象。这些问题的共性都是AI在一个信息孤岛里做决定。这也是为什么同一个AI在空项目里表现惊艳一旦放进老项目就各种别扭。它缺少的从来不是代码能力而是对现状的感知。2.2 风格与抽象断层局部合理全局混乱第二个根源是风格和抽象层级。AI生成的单块代码看着挺合理放回项目里就格格不入。最典型的是抽象层级混乱该抽函数的地方写了一大坨顺序逻辑该用现成抽象的地方自己手搓了一个相似的轮子。我见过最夸张的一次是让AI写一个文件上传组件它在组件里直接处理了Blob转Base64、分片断点、重试逻辑整整三百行。不是不能跑而是这些逻辑本来应该放在数据层或工具层结果全部堆进了UI。这种代码一旦合进去后面的维护成本是灾难级的。说白了AI很会把“一个功能”写成“一个函数”但它很难理解“一个功能”在现有工程里应该分布在哪些层次。这个断层在生成阶段不容易暴露要等收尾、做代码审查时才看得清清楚楚。2.3 验证空转代码会写但不会证明自己能用第三个根源也是我觉得最要命的AI生成的代码天然缺少验证。它不会主动跑构建不会想边界不会告诉你“这个函数在空列表时会崩”“这个接口在鉴权失败时会返回误导性错误”。你问它“有测试吗”它会给你补一个只测happy path的demo你问它“考虑过并发吗”它会说“应该没问题”。这不能怪AI因为模型的训练目标就是生成“看起来正确”的文本不是生成“被验证过正确”的系统。这意味着AI把验证成本转嫁给了你。代码能跑不代表它理解了对错而你偏偏又因为“AI写的代码”放松了警惕——因为你没参与它的推导过程反而更难判断哪里可能是坑。这也是“最后一步”尤其需要系统化方法的原因。维度手写代码的自然状态AI生成代码的常见状态上下文获取边写边感知项目全局只有需求片段缺少仓库视角抽象层级写的过程中自然分层倾向于堆进单一函数边界处理边写边想异常分支默认“美好路径”验证过程写一步验证一步代码生成完就当任务结束3. 把收尾变成四步清单从“烦”到“照做就能推进”3.1 结构审查不要逐行读代码先让AI补一份变更清单拿到AI生成的代码我最开始的做法是逐行读结果经常读着读着就被带进它的思路里根本跳不出来。后来我换了个方式先让AI自己总结一份变更清单包括它新增了哪些文件、改动了哪些函数、用了哪些外部依赖、假设了什么条件。这个做法的好处是把“读代码”变成“对照清单”。你不需要全盘读只需要拿着清单去项目里核对这个函数项目里是不是已有替代这个依赖是不是必须的这个假设能不能成立AI生成代码时的心智模型会通过清单暴露出来你审查的效率会高很多。有一次AI在清单里写了一行“假设用户角色已经由中间件注入”我立刻意识到这个假设在目标接口里并不成立当场就避免了一个权限漏洞。这就是让AI先交清单的价值。3.2 让构建失败当你的依赖地图代码审查解决的是“这代码该不该存在”而构建解决的是“这代码能不能跑起来”。我现在的习惯是结构审查之后立刻把代码放进真实项目里跑构建和静态检查让编译器、类型检查器、lint工具帮我列问题清单。这个问题清单非常值钱缺了什么依赖、哪个类型不匹配、哪个import不存在、哪个变量没用到全部清清楚楚。我只需要按图索骥一条一条修。说句实话很多AI写代码的报错本身就是最好的“依赖地图”比你自己盲猜快得多。这里提醒一句一定要用项目自己的构建命令别用AI建议的命令。AI生成的脚本配置文件里经常藏着它自己造的东西直接用项目的统一入口跑一遍才能暴露真正的集成问题。3.3 测试要有优先级先补会回归的再补好看的不是所有AI生成的代码都要补满测试。之前我走过弯路逼自己给每个新函数都写几个用例结果时间全花在测试那些“永远不会出错”的纯函数上真正需要保护的逻辑反而没覆盖。经过一段时间调整我现在的优先级是三条第一优先级跟旧行为冲突的地方。比如改了现有函数的调用方式、换了数据结构这些一旦出错会直接影响线上功能。第二优先级核心业务逻辑。特别是那些带条件分支、循环、外部依赖的代码很容易出现边界遗漏。第三优先级纯工具函数。简单到一眼能看清的回归用例可以用但不必钻牛角尖。按这个顺序补收尾时间能省一半而且真能拦住回归。3.4 合入前最后一件事切分提交与写人话说明最后一步是把AI那一大坨改动变成别人能review的提交。AI生成代码时通常一次性给你几百行改动如果直接一个提交怼上去reviewer大概率会崩溃。我会按功能边界拆成多个提交每个提交只做一件事。提交说明也要写成人话而不是“update code”“fix bug”这种。我通常用这个结构feat(log-export): support CSV export of filtered logs Why: 运维需要从日志平台导出指定时间段的数据做离线分析 What: 新增导出接口和前端按钮复用已有的日志筛选组件 Notes: 时间字段为空时按空字符串导出不中断任务这样几行字说清楚背景、内容和注意点比什么都强。AI写的注释往往只解释“代码在做什么”但不解释“为什么这么做”项目里真正缺的是后者。我把这套收尾流程固化成了一个检查清单每次合AI的代码时照着走一遍步骤动作通过标准结构审查AI变更清单 项目现状核对无多余依赖、无重复轮子构建修复跑项目统一构建与lint所有报错清零测试补全按优先级补回归用例核心分支有覆盖提交整理拆分提交 写人话说明每个提交可独立review4. 收尾的脏活可以外包让AI和脚本替你跑腿4.1 写代码前先逼AI交“变更计划”收尾从第一步就变轻我后来发现一个特别有用的招式别一上来就让AI写代码先让它交“变更计划”。把“写一个xx功能”改成“先告诉我你打算改哪些文件、动哪些函数、依赖哪些模块、有什么风险我确认后你再写”。这个习惯帮我规避了大量无效生成。因为AI一旦先做计划就会被迫模拟思考整个改动范围很多上下文盲区在写之前就暴露了。而且我确认计划时可以顺手把项目里已有的实现告诉它让它复用而不是重造。实际用下来这招让收尾时间至少砍掉三成强烈建议试试。4.2 让AI扮演审查官自己审自己把AI生成的代码再丢回给它让它换个身份审一遍也是个非常实用的做法。我会这样提问你现在是一个资深代码审查者请以合入主干前最后一轮review的视角 检查上面这段代码重点看 1. 是否与项目常见结构一致 2. 有没有隐藏的边界问题 3. 有没有可以复用现有模块的地方 输出问题清单不要修改代码。AI给自己找茬的效果经常比它直接写代码的效果还好。因为它能站在“挑毛病”而不是“完成需求”的角度重新看一遍很多刚才没考虑到的分支会被挖出来。我还会追加一句“假设项目里已有xxx工具函数这段代码应该怎么调整”相当于提前做了一次人工审查演练。4.3 用一条脚本把格式化、静态检查、测试全部串起来高频的收尾操作值得变成一个命令。我通常会写一个简单的收尾脚本放在项目里把格式化、lint、静态检查、构建、测试一条龙跑完。拿Python项目举例大致是这样的#!/usr/bin/env bash set -euo pipefail echo formatting ruff format --check . echo lint ruff check . echo type check mypy src/ echo test pytest -q前端项目就把prettier、eslint、tsc、vitest对应替换掉。有了这条命令收尾的“验证”部分变成了一个动作而不是十个动作。这也顺便解决了人懒的问题——一个命令能跑完的事人不会拖太久。4.4 把收尾标准写进项目说明AI每次都会自己注意如果团队里多人用AI写代码最好把收尾标准写进项目根目录的说明文件里让AI在每次生成时参考。比如这样一段# 项目约定供AI参考 - 新增功能前先查询项目中已有的工具函数禁止重复实现 - 所有时间字段必须处理空值不允许直接访问字典中的key - 函数命名采用snake_case类型别名统一用XxxType格式 - 任何对外接口必须有docstring说明输入输出和可能的异常有了这段说明AI生成代码时的表现会明显好一截。收尾的问题不是“代码写得烂”而是“AI不知道你的标准”把标准前置比事后修要轻松太多。5. 拿日志导出功能当例子一次合入的完整收尾记录5.1 需求与AI初稿功能能用问题一堆为了把前面这套流程讲透我用一个实际经历过的例子复盘。需求很简单给日志模块加一个“筛选后导出CSV”的接口。AI给的第一版代码长这样def export_logs_to_csv(logs, filepath): with open(filepath, w) as f: f.write(timestamp,level,message\n) for log in logs: f.write(f{log[time]},{log[level]},{log[msg]}\n) return filepath功能确实能跑日志列表传进来就能写文件。但这版代码距离“可以合入主干”还差着好几个身位。5.2 我在收尾阶段修掉的几类典型问题第一类是重复实现。项目里其实已经有一个统一的CSV写入工具封装好了编码处理、表头检查、路径自动创建AI这一版完全没有用。我让AI先列变更计划时它列出的文件里根本没有检索现有工具的任务这就是上下文盲区的典型表现。第二类是边界缺失。log[time]直接硬取一旦某条日志的时间字段为空整个导出任务直接崩掉。日志系统里出现脏数据是常态这个问题非常致命。第三类是风格不一致。项目里所有日志对象都统一用content字段表示消息AI写的是msg函数命名也不是现有模块的snake_case风格。虽然无伤大雅但合入后看代码的人会非常难受。第四类是编码问题。生成时用的是普通open在Windows上导出的CSV用Excel打开会乱码。这种细节AI没经过实际环境根本想不到。5.3 四步清单在实际合入中的顺序和时间分布我按前面说的四步走了一遍结构审查阶段对照AI的变更清单发现它计划新增一个工具函数但项目已有一个功能重叠直接砍掉这个动作大约用了10分钟。构建修复阶段跑完脚本后蹦出来三个类型错误都是字段名不一致导致的逐个改掉大约15分钟。测试补全阶段我给导出接口补了三个用例正常筛选导出、时间字段为空不中断、文件路径不存在时自动创建目录大约20分钟。提交整理阶段本来AI把所有改动堆在一个文件里我拆成了“导出接口”和“工具函数抽取”两个提交并写了说明大约10分钟。合计下来收尾大概55分钟。对于一个真正要进主线的功能来说这个时间完全可以接受。5.4 有工作流和没工作流的对比不用流程的时候我面对这堆问题往往是“看到哪个改哪个”一会儿回去改代码一会儿去问AI一会儿发现测试挂了毫无章法两个小时起步改完还不确定是否漏了东西。用了四步清单之后整个过程变成了过检查点每一步都有明确的完成标准。速度稳定质量也稳定。最关键的是我不需要靠“状态好”才能高效收尾——流程本身就拉着我走。这才是工程化方法该有的价值。对比项没有固定流程有四步清单收尾耗时不稳定经常2小时稳定在1小时内遗漏风险靠临场反应容易漏边界按清单逐项过提交质量改动揉成一团提交边界清晰个人体验焦虑常想放弃AI可控愿意长期用6. 收尾做顺之后我对AI编程的几个认知更新6.1 写代码的价值在下沉审查和整合的价值在上升这几年越来越明显的一个趋势是“把代码写出来”这件事正在变便宜。AI写代码的门槛越来越低谁都能让它生成一段能跑的代码。但“这段代码值不值得留在项目里”的判断仍然只能由人来完成。一套成熟项目里最缺的不是能生成更多代码的人而是能把代码以正确姿势放进去的人。收尾不是苦力活它就是工程判断力本身。想清楚这一点之后我开始认真对待收尾这一环而不是把它当成“AI编程的缺点”来忍受。6.2 把AI当“初稿编辑器”而不是“同行程序员”我现在的心态已经变成AI给的永远是初稿是快速生成的草稿纸不是可以直接签发的成品。这个认知更新之后很多东西都顺了。我不再期待AI一次写对也不因为它出错而失望我会带着“编辑和出版”的思路去对待它的输出把审查、修改、验证当成正常流程而不是额外负担。这种心态还有一个附带的好处当AI生成的代码质量一般时我不会被“它都写出来了我应该直接用”绑架。该重构就重构该删就删代码所有权始终在自己手里。6.3 单人收尾靠习惯多人协作靠纪律如果是自己一个人用AI写代码养成一套固定的收尾习惯就够了。但如果是团队协作收尾必须上升成纪律。AI生成代码的随意性很容易被放大一个人合入一个风格混乱的PR后面五个人都要给它擦屁股。我见过一个比较健康的做法团队里明文规定AI生成的代码必须走和手写代码完全一样的审查、测试、提交流程不允许有任何特殊通道。这个纪律谈不上创新但非常有效。它把AI定位成“提高生产效率的助手”而不是“绕过工程规范的漏洞”。最后再分享一个小习惯。我现在每次让AI写代码之前会先写一段自己的“完成定义”相当于把最后一步的标准提前到第一步。写完代码先问自己如果我今天必须合入它我还缺什么而不是代码能不能跑这个小变化帮我省下的时间远比预想的多。希望这个思路也能帮你在AI编程的路上少几次望着收尾叹气的时刻。