
1. 为什么最后一步总卡住AI写代码的“最后一公里”困局用AI写代码大半年我最烦的真不是模型给我写不出来而是每次写完之后的收尾。那个所谓的“最后一步”就是把AI生成的东西弄进现有项目、跑通所有测试、处理完各种冲突最后干干净净地提交上去。AI写代码越快这一步的慢和卡就越刺眼。这篇我打算把“最后一步”拆开讲清楚结合我实际踩过的坑聊聊现在固定下来的收尾工作流和几条土办法适合每天都在跟AI结对编程、但一到合并就头疼的人。1.1 AI生成代码的三张面孔AI生成代码最大的问题不是“看起来不能跑”而是“看起来太完整”。它默认你项目里有那些它假设出来的函数、配置项、中间件但现实往往没有。比如让它写一个订单导出的定时任务它可能会调用一个并不存在的消息队列客户端然后自动往依赖文件里加了一个高版本库。这些前提在一开始跑demo时根本不会暴露等你想合并进主干集成测试一堆红你才意识到它在很多地方做了“无意识假设”。第二张面孔是风格不稳定。同一个AI写出来的代码这次用单引号、下次用双引号这次变量叫order_info、下次叫order_data。单看都还行放到一个统一规范的项目里就非常碍眼。你花在格式对齐、命名统一、import排序上的时间经常比写功能本身还长。这类问题单独看都不致命但会在最后一步变成密密麻麻的diff噪声直接把代码评审淹没。更麻烦的是AI并不会因为你在上一个文件里用了某种规范就自动在下一个文件里保持同样习惯它每次都是“重新开始”的。第三张面孔是上下文丢失。AI通常只能看到你最近粘贴的代码片段项目里跨模块的约定——比如全局异常要抛统一格式、所有对外接口都要做幂等校验、日志不能打印敏感字段——它很难主动遵守。在生成阶段这些约束缺失不会立刻报错但会在收尾阶段集中爆发。我见过最典型的情况AI生成的代码单测全过一接上核心流程才发现它绕过了统一的鉴权中间件。这就像外包临时工活是干完了可压根没接你验收的标准。1.2 最后一步到底包含什么很多人以为最后一步就是点部署按钮但在和AI协作的场景里这一步远不止一个动作。它是从“AI交出一段能跑的代码”到“这段代码真正进入主干并且稳定运行”之间的一整串工序合并分支前解决冲突静态检查与格式统一依赖与环境对齐自动化测试与全链路回归变更说明与代码评审构建发布与线上验证还有随时准备回滚的预案。为什么最后一步最烦因为生成阶段的“爽”和收尾阶段的“不确定性”反差太大。AI给你快速铺了几百行代码看起来离上线只差一步真到收尾你会发现每一步都有可能让你回退重来。你没法像生成代码时那样无限重试因为代码已经接进项目了改坏的成本是双倍的。前面效率越高最后收口越难这就是典型的“最后一公里”困局。还有一个隐藏原因AI生成速度太快你很容易跳过“写设计说明”“定接口契约”“明确边界条件”这些过程。前面省掉的东西最后一步会全部换成问题找回来。2. 合并前、提交前、发布前三个阶段的收尾实操2.1 合并前先做静态体检AI代码别直接进主干我的第一个原则是AI生成的代码绝不能直接从编辑器拖进主干。无论AI工具表现得多么自信先把分支拉到本地做一轮“静态体检”。所谓静态体检就是不用跑业务逻辑先让代码库本身的检查工具过一遍格式统一、静态分析、类型检查。这些检查能拦住一大类低级问题比如未使用的变量、明显的空指针、风格差异。别小看这一步它能把合并时的冲突范围从“到处都改了”缩小到“只有真正动逻辑的地方”。我习惯把静态体检挂在提交钩子里让每次提交前都自动跑一遍。下面是一个非常朴素的配置核心思路是保留格式检查和静态检查两关你完全可以改成自己项目里的命令再放进版本库repos: - repo: local hooks: - id: format-check name: format-check entry: make format-check language: system types: [text] - id: lint-check name: lint-check entry: make lint language: system types: [text]有人觉得提交钩子麻烦但实际用下来收益极大。AI生成代码的格式差异比你想象得多一旦这些差异混进diff评审的人根本没心思看逻辑。这里有个细节pre-commit跑的是本地命令如果团队成员本地环境不一致还是要以CI的检查结果为准本地钩子只是让大家尽量少踩坑。如果你把格式检查、静态检查全部交给AI在生成阶段“顺手做好”那不现实——AI对项目既有格式的敏感度很低换个模型版本甚至会出现同一段代码两种风格的情况。2.2 对版本、锁依赖解决AI代码“在我机器上能跑”AI最爱在依赖上动手脚。它看到代码里用了某个库就可能悄悄在依赖文件里加一行最新版本。这个版本在你本地恰好能下载但到了CI或者生产环境受网络源、系统库版本影响很可能就装不上或行为不一致。所以我收到AI改动后第一件事永远是检查依赖文件的diff凡是没有明确理由的依赖变更一律撤回。很多AI编程工具都自带“自动安装依赖”的功能看起来贴心其实隐患很大。它不会考虑你项目里另一个模块是否依赖这个库的旧版本也不会考虑生产环境的网络策略。我见过一次事故AI为了用一个新的时间处理函数把日期工具库从稳定版本升级到全新主版本结果很多格式化结果全变了线上数据差点出问题。所以不要轻信“自动补依赖”先看它为什么补。如果你项目里有锁文件记住锁文件也要纳入版本控制。锁文件的作用是把所有依赖的精确版本固定下来避免“我机器上是1.2.3线上装了个1.2.9”这种玄学问题。检查依赖时我会用几个命令快速过一遍git diff requirements.txt pip list --outdated npm ls如果发现解析出的依赖树里有同一个包出现多个版本或者某个间接依赖版本异常先解决它再继续。把运行环境做成可复现的镜像或虚拟环境也很必要。这一步说白了就是把“能跑”和“能交付”分开前者是AI给你的幻觉后者才靠得住。2.3 全链路测试与数据回放比AI单测更靠谱的收尾验证AI生成的单元测试经常是“自证清白”式的它知道自己生成了什么参数于是写一个恰好通过的测试。这种单测可以当参考但不能当验收依据。我建议在收尾阶段至少补一个跑通核心链路的最小冒烟用例。比如一个订单模块冒烟用例就是创建订单、查订单、更新状态、再查一次。这个用例不需要覆盖所有分支只需要证明改动没有把主路堵死。def test_core_flow(client): resp client.post(/api/order, json{item: note, qty: 1}) assert resp.status_code 200 order_id resp.json()[id] detail client.get(f/api/order/{order_id}) assert detail.status_code 200 assert detail.json()[status] created如果项目里已经有历史流量或脱敏数据可以拿一部分做数据回放。把真实输入喂给改动后的接口对比关键字段是否和预期一致。这一步不需要很重能从线上捞一份脱敏请求更好没有的话就把之前出过故障的用例整理成一个固定回归集每次AI改动后跑一遍。数据回放的价值在于它比任何测试数据都更接近真实情况能发现AI代码里那些“看起来合理但实际和业务规则不符”的问题。给团队的测试基座不一定要很大但至少要把核心链路用冒烟用例锁住。我见过很多项目一开始觉得“我们很早就全面AI编程测试不重要”结果每次合并都变成大型翻车现场。后来补了两三个核心链路冒烟用例收尾时间立刻降了一半。别贪多先把最关键的两三条链路锁死。2.4 提交信息与代码评审让AI改动有据可查最后一步里最容易糊弄的就是提交信息。AI生成了一堆代码很多人懒得写清楚直接一个“update”推上去。等三天后自己回来查都觉得懵更别提评审的人。我现在的做法是把提交信息当成变更说明书它必须说清楚改了什么、为什么改、哪些部分是AI生成、哪些是人工调整。这个习惯对日后排查问题帮助极大因为你能快速知道某段AI代码当初是在什么背景下进来的。下面是我常用的提交信息模板你完全可以改成适合自己项目的文字feat(order): add export job - add scheduled export task - add retry policy for failed exports - AI generated: export task skeleton - manual changes: integration with existing queue评审时也别逐行盯。AI生成的代码逐行看能把人看瞎。重点看几类位置授权与权限校验、外部接口调用的超时与重试、日志里有没有打印敏感字段、异常分支是否吞错。如果项目允许可以在合并请求描述里直接贴出diff摘要让评审者先看摘要再决定要不要点开具体文件。这套动作看着琐碎但它让“最后一步”从“碰运气”变成“有流程”。2.5 发布与回归验证别让最后一步变成线上事故合并通过不等于交付完成。发布前要确认健康检查、启动参数、外部依赖都已经就绪。如果条件允许采用灰度发布先放少量流量观察错误率和耗时确认稳定后再全量切。这不是操作门槛而是心理缓冲——AI生成的代码即使测试全过也可能在真实流量下暴露性能或边界问题灰度能让你在影响可控时发现它。发完版至少观察一个完整业务周期重点关注核心接口错误率、慢请求比例、内存水位三项。还要保留上一版本的产物方便随时快速回滚。我在项目里通常会把回滚命令写进发布文档别等出事的时候现翻历史记录。这一步做好才算真正把最后一步合上。最后再多说一句AI写代码可以很快但发布这步真的快不得宁可多观察半小时也别让线上告警变成你的闹钟。3. 实操心得一套能坚持下来的收尾清单与AI协作姿势3.1 一条命令跑完收尾用自动化脚本兜住“最后一步”把上面这些流程串起来最有效的方式是做成脚本让收尾变成一个可重复执行的命令。我会在项目根目录放一个Makefile里面定义一个verify目标按顺序跑格式检查、静态分析、单元测试、核心冒烟、打包。这样每次从AI分支准备合并前只要执行一条命令系统会替我把所有该想起的事情跑一遍。verify: format-check lint test smoke package顺序很重要先跑快的再跑慢的。如果格式检查先失败就不用浪费时间跑几十个测试了。另一个隐藏好处是脚本会形成肌肉记忆只要这条命令能过收尾就有七成把握没过你就知道问题卡在哪一步不用瞎猜。脚本一定要纳入版本控制不然时间一长大家又会回到手动敲命令的老路。有些人会觉得“我也写了脚本但没过多久又不用了”原因通常是脚本跑起来太慢或输出太乱。改进办法是给脚本拆成多个短目标像verify依赖的小目标可以单独执行并且只打印错误部分的摘要不要把几千行日志全部怼到屏幕上。自动化不是越复杂越好而是越容易让人愿意执行越好。3.2 给收尾留两倍时间被低估的时间预算用AI写代码省下来的时间往往会给人一种“今天最多干半天”的错觉于是你把剩下的时间排了新需求。但AI省下的只是“生成”的时间并没有省下“验证”的时间。我现在的经验是AI生成花多久收尾至少留两倍时间。比如生成一个模块花了10分钟那合并、测试、评审、发布准备至少预留20到30分钟如果涉及多个模块时间还要翻倍。这个时间预算不能只是心理估计要体现在排期里。我会把“合并到主干的准备”单独列成一个任务而不是笼统算在开发里。这样团队成员一看板就知道某次改动还没到可以合入的阶段。收尾阶段也不要同时开新功能因为注意力需要从“创造模式”切换到“检查模式”来回切换的损耗比想象中大很多。我还有一种比较土的时间管理方式每次AI生成完代码先关掉代码生成窗口给自己倒杯水然后开始收尾。这个动作看起来很小但能让你从“继续往前走”的心态切换成“回头检查”的心态。收尾最怕的就是急一急就会跳过检查赌运气然后被线上问题狠狠教育。3.3 让AI解释报错而不是继续写代码收尾阶段的AI协作姿势很多人收尾卡住之后第一反应是继续让AI“帮我修复”。这个动作很危险因为它等于把最后一步的钥匙又交回给一个不太了解全项目的模型。我的姿势是把报错日志、当前分支的代码、上下文约定一起贴给AI先让它解释问题产生的原因再让它给出最小修复方案并且明确告诉它范围不许扩大。下面是我的报错日志和涉及的核心代码。请先解释最可能的原因再给出最小修复方案。只修改和这个报错直接相关的部分不要重构其他函数不要改动依赖版本。为什么这样有效因为收尾阶段缺的不是代码生成量是定位问题的精确度。AI擅长从报错日志里找出线索也擅长解释一段代码的行为但它不擅长在没有约束的情况下做全局优化。你把范围缩小它反而能给出更靠谱的答案。这套姿势用下来我修错误的次数少了很多因为不会再出现“修一个bug带出三个新bug”的连锁反应。如果AI给的解释让你觉得模棱两可那就再贴一段运行时日志给它让它给出具体的变量值和函数调用路径。收尾阶段最忌讳“感觉是这里的问题先改改看”一定要让AI给出基于日志证据的判断。你可以把它当成一个逻辑助手而不是代码生成器用这种方式协作最后一步会顺很多。4. 常见问题与排查技巧实录4.1 最后一步高频问题速查表先对照再动手做表格之前先强调一个原则排查最后一步的问题永远先看日志和diff不要先动代码。日志会告诉你失败在哪一步diff会告诉你AI到底改了什么。下面是我见过最高频的几类问题按排查顺序列成一张表你可以直接当作清单用。高频问题常见原因排查顺序与解法本地跑得通CI挂掉环境变量、依赖版本、时区差异先看CI失败日志再对比锁文件最后查环境变量配置git冲突爆炸AI基于旧主干生成和最新改动重叠合并前先更新主干冲突时先rebase到最新再逐段合并单测过联调失败AI只验证了局部函数没验证链路补一个核心链路冒烟用例定位断点依赖文件被AI偷偷改AI为了“补依赖”自动加版本diff依赖文件恢复无关联的变更部署后接口大量报错配置项或环境变量被AI改动检查启动日志和配置diff必要时立即回滚测试用例随机失败AI测试共享状态、依赖执行顺序给测试数据加唯一前缀保证用例独立这张表不一定覆盖你遇到的所有问题但可以确定的是大多数最后一步问题都能通过“先看日志、再看diff”这两步定位到七七八八。如果你卡了很久没头绪多半不是技术问题而是你还没把问题描述清楚。把报错原文、相关代码、预期行为写下来AI反而是很好的排查帮手。4.2 我踩过的坑和现在的避坑习惯第一个坑是AI说“修复了”但实际没跑测试。之前有次测试红了一片我问AI改了哪里它秒回一段代码并说已修复。结果我跑一遍还是同样报错。后来我养成习惯AI给修复方案我一定先自己跑那条测试命令看到绿色再往下聊。在这件事上AI的“承诺”不可信只有命令输出可信。第二个坑是收尾阶段让AI顺手重构。有次让AI修一个接口的空指针它把整个函数签名都改了顺带改了三个调用方。最后一步从修bug变成了小型重构回归成本直接翻倍。现在的习惯是给AI划文件白名单只允许它改指定文件其他文件一律不碰。这个约束用一句话就行“只允许修改某某文件其他文件不要动。”第三个坑是敏感信息混进代码。AI参考示例时很容易把像密钥、连接串、内网地址这种东西带进代码。我的习惯是提交前用脚本扫描常见敏感关键字像api_key、password、secret、token一旦命中就先拦下来。这个动作虽然简单但能避免很多线上事故。尤其是AI生成配置文件时你根本不知道它从哪里借鉴了一段真实配置。第四个坑是跳过评审直接合并。最后一步通常赶时间越急越想跳过评审。但AI生成代码的评审价值比人写代码更高因为它不了解项目上下文。现在我会给主干分支设置保护规则没有通过评审的合并请求不能入主干。别觉得麻烦这相当于最后一道安全带。评审不是走形式而是用人的判断去补足AI缺失的上下文。现在团队里有一条很土但有效的“完成定义”本地验证通过、静态检查通过、依赖锁文件一致、核心链路测试通过、变更说明已写、评审通过。六条全过才允许把分支合并并触发发布。有了这个定义最后一步不再靠感觉而是变成一道可勾选的工序。我的体会是用AI写代码真正拉开差距的地方就在这个最后一步。谁能把收尾做得越自动化、越可预期谁就越敢让AI多干活。最后再分享一个土办法每天下班前把最后一笔代码收进一个干净分支第二天早上合并前先给自己泡杯茶用稳定心态面对最后一公里的各种意外。这一步顺了一整天的代码节奏都会顺。