资讯动态

ChatGPT辅助编程:加速开发与代码审查的工程实践

发布时间:2026/10/27 13:23:44 来源:尧图企业网站定制
凌晨一点半屏幕上是一段跑不通的分页逻辑。我把它整段贴进对话框补了三句话这是报错堆栈这是表结构别改我的函数签名。三十秒后回来的版本依然有错但它把问题从我不知道从哪下手变成了我知道该看第几行。这个转变就是我现在愿意把 ChatGPT 放进日常开发链路的原因。所谓用 ChatGPT 编写代码说白了不是让它替你写完整个项目而是把它当成一个随叫随到、读过海量代码、但从来没真正跑过一行程序的人——让它负责把从零到能跑这段最费手的时间压下去你负责把跑得对不对这道关守住。加速开发和减少错误这两件事其实是一体两面省下来的时间必须重新投到验证上否则省下来的时间会以线上事故的形式还回去。这篇东西写给三类人看刚开始用对话式工具写代码、还被它的自信误导的新手已经用过一阵、但总觉得生成的代码不敢上线的中间层以及想把这套方式推给团队、又怕质量失控的技术负责人。下面全部是我自己踩过坑之后沉淀下来的流程、模板和判断标准能直接抄。1. 先把期待摆正它到底加速了哪一段1.1 一段代码从想法到合并时间根本不在敲键盘上很多人对加速开发的想象是打字变快其实真正的耗时结构完全不是这样。一个中等复杂度的功能从接到需求到合并进主干时间大致分成几块理解需求和确认边界、查文档和确认接口、写第一版能跑的代码、调试到跑通、补测试、评审返工。真正敲字符的时间可能只占两成剩下的全在想清楚和验证对上。AI 工具能压缩的主要是查文档确认接口和写第一版能跑的代码这两段顺带把调试到跑通里的定位环节缩短一些。它压缩不了理解需求因为它不在你的业务现场它也压缩不了验证对因为它没有运行环境也没有你的数据。想明白这一点用法就变了。你不会再指望它一次给出完美方案而是会把它安排在那些机械但需要查资料的环节上样板代码、格式转换、正则表达式、SQL 拼装、单元测试骨架、报错解释、陌生库的入门示例、跨语言翻译。这些活儿的共同点是有明确的正确性判据且不需要太多隐性业务知识。反过来凡是需要知道我们这套系统里那个字段其实有三种脏数据的活儿它一定干不好因为它不知道。我在实际项目里做过一个粗略的对比一个带分页、重试、字段映射的导出接口自己从头写大约要两小时其中一半时间花在翻文档确认某个库的参数名把接口签名和数据结构先定死再让它生成骨架和测试整体压到四十分钟左右但后面人工审查和补边界条件又花掉半小时。净收益大概是一半时间前提是你得会审查。不会审查的人用这套方法收益是负的。1.2 三个实打实的收益我确实吃到了第一个收益是冷启动。接手一个没接触过的语言或框架最痛苦的阶段是不知道惯用写法长什么样。这时候让它生成一个最小可运行示例比翻半天官方文档快得多因为示例是围绕你的具体场景生成的你能立刻看到结构。我试过用一个完全陌生的模板引擎做渲染让它先给一个读取列表、循环渲染、处理空数据的完整片段五分钟就有了可运行的原型剩下的时间全用来理解它为什么这么写。第二个收益是测试补齐。写测试这件事很多人不是不会写是懒得写。让工具根据函数签名和分支逻辑先生成一版用例你再往里补业务边界效率提升非常明显。它生成的用例往往覆盖不到你真正关心的脏数据但骨架、断言写法、mock 结构这些体力活它包了你只需要做加法。第三个收益是报错定位。把完整的堆栈、相关代码片段、依赖版本一起贴过去让它给出三到五个可能原因然后你逐个排除。它的猜测不一定对但经常能提供一个你没想到的方向尤其是那些语法完全正确但行为诡异的问题比如时区、编码、浮点精度、异步顺序。1.3 三个必须认账的代价不认就会出事第一个代价是自信的错误。它写错的代码和写对的代码语气一模一样没有任何我不确定的标记。新手最容易在这里翻车看着逻辑通顺、命名规范、注释齐全就直接用了结果某个参数在新版本里早就改名了。我的应对办法是硬性规定任何涉及外部库调用的代码必须自己跑一遍任何涉及金额、权限、删除操作的代码一行都不许直接用。第二个代价是上下文污染。一段对话聊得越久早期的约束越容易被稀释。你在第一轮说了不要引入新依赖聊到第十轮它可能就给你import了一个没装过的包。这不是它故意违反而是长对话里的信息权重会衰减。解决办法后面会讲核心是每轮固化结论、关键约束重复贴。第三个代价是审查成本转移。生成代码变便宜了审查代码变贵了。你省下的敲键盘时间会以读别人代码的形式花出去而且读的是风格不统一、可能夹带私货的代码。如果团队没有评审习惯这套工具带来的净效果就是代码量膨胀、质量下降。2. 机制层面搞清楚它为什么会对又为什么会错2.1 一个读过海量代码、但从没运行过一行的实习生对话式模型的工作方式本质是根据已有的上下文一个片段一个片段地推测接下来最可能出现什么。它读过极大量的公开代码所以对常见的写法长什么样有极强的模式识别能力。你给它一个函数名、几个参数、一句注释它能补出符合社区惯例的实现这就是它好用的根源。但它优化的是像不像不是对不对。它没有编译器没有运行时没有你的数据库也没有你的日志。它不知道那个 API 在你的版本里已被废弃也不知道你上游传进来的id可能是空字符串。用实习生类比最贴切这个实习生读完了图书馆里所有的编程书能背出各种设计模式但从来没在一个真实项目里提交过代码。他写的东西结构漂亮、注释规范你让他解释他也能解释得头头是道但你必须替他把代码跑一遍。你越早接受这个设定用起来越顺。2.2 上下文窗口与信息衰减为什么必须学会喂重点模型一次能看见的内容是有上限的这个上限用 token 计量。中文一个字大约一到两个 token代码里符号密集token 消耗更快。这意味着你贴一整个几百行的文件进去大部分内容其实是在挤占预算真正关键的约束反而被淹没了。更麻烦的是位置效应放在超长上下文中间部分的信息被准确利用的概率明显低于开头和结尾。实操上的结论很直接。第一只贴相关片段别贴整个文件把无关的函数折叠掉用注释标注此处省略若干行。第二把最重要的约束放在最前面并且在结尾再重复一遍比如开头写约束不得引入新依赖结尾再写再确认一次不许新增依赖。第三长对话要主动结算聊到一定轮次就让它输出一段当前确定的方案与未决问题的总结然后开新对话把总结贴进去当起点。第四贴报错要贴全从异常类型到最内层调用栈缺一行都可能导致它猜错方向。2.3 幻觉在代码里的四种典型形态形态具体表现危险程度识别方法编造接口调用一个不存在的方法名或参数名拼错但看起来合理高跳转到定义或查官方文档确认签名版本错配用旧版本的写法或用了新版本才有的参数高核对项目锁定的版本号查该版本的变更记录边界错误主体逻辑对但空值、零、负数、超长输入处理错最高逐条过边界清单补测试注释与实现不符注释说失败会重试三次代码里根本没有重试中读代码而不是读注释这四类里第三类最难发现也最致命。因为前面三类你只要跑一次或者查一次文档就能暴露而边界错误往往要等到特定数据出现才炸。我吃过的一次亏是批量处理脚本它写的分批逻辑在数据量能被批大小整除时完全正常一旦有余数最后一批就丢了几条。测试数据是我随手造的整数条正好没触发。后来补了数量为批大小加一的用例才暴露出来。3. 任务分级哪些活交给它哪些必须自己扛3.1 用确定性 × 耦合度做一张分工表判断一个任务该不该交给它我只看两个维度这件事有没有明确的验收标准确定性以及这件事牵扯多少隐含的系统上下文耦合度。两个维度一交叉四象限就出来了。低耦合高耦合高确定性放心交正则、格式化、类型转换、单测骨架、文档注释半交半审接口骨架、数据映射、查询语句低确定性只当参考技术方案对比、架构建议、性能猜测基本别交核心业务规则、状态机、并发控制左上角是它的主场有明确对错、不需要知道你的系统长什么样比如把这段 JSON 里的驼峰字段转成下划线跑一遍就知道对不对。右下角是雷区比如订单在什么状态下允许部分退款这种规则藏在业务流程里它写出来的东西看着合理但一定漏了你没想到的例外。3.2 我的默认黑白名单白名单也就是默认直接交给它的正则表达式配测试字符串、日期与时区转换、CSV/JSON/XML 互转、SQL 单表查询与聚合、接口的错误码映射表、日志解析脚本、单元测试骨架、代码注释与文档、把一个函数从语言 A 翻到语言 B、解释一段看不懂的遗留代码。黑名单也就是默认自己写的权限判定、金额计算与舍入、库存扣减、并发与锁、事务边界、缓存失效策略、涉及删除的批量操作、性能关键路径上的循环优化、任何需要读三个以上内部模块才能判断的改动。中间地带最需要经验数据映射和接口骨架可以交给它但你必须自己定死输入输出的数据结构和字段语义让它只填中间的转换逻辑。这个定死的动作恰恰是最不能省的部分。3.3 判断标准简化成一句话如果非要压缩成一句可执行的判断这件事出错之后我能不能在十分钟内发现并回滚能就大胆交给它用测试兜底不能就自己写或者只让它写不涉及关键判断的部分。这个标准比任何AI 能力清单都实用因为它衡量的是你的风险承受力而不是模型的能力边界。4. 提示词把模糊需求翻译成可执行指令4.1 五段式模板我现在基本照这个填写提示词和写技术方案是一回事含糊的输入只会得到含糊的输出。我惯用的结构是五段角色与目标、上下文、约束、输出格式、验收标准。这套结构的好处是把我以为你懂的部分全部显式化减少来回拉扯。【角色与目标】 你是熟悉 Python 3.10 的后端开发者。目标实现一个把图片元数据批量整理成 CSV 的函数。 【上下文】 - 现有项目使用 requests csv 标准库不引入其他第三方依赖 - 输入是 list[dict]每条包含 id、width、height、tagslist[str]、created_atISO8601 字符串 - 已有一个工具函数 safe_get(d, key, default)可以直接调用 - 现有代码风格4 空格缩进、类型注解齐全、异常向上抛 【约束】 - 不得修改公开函数签名export_metadata(records, path) - int - 不得引入新依赖 - 空输入必须返回 0 且不创建文件 - 单条记录缺字段时跳过并计数不允许整批失败 - 不得吞掉异常IO 错误必须抛出 【输出格式】 1. 完整函数实现 2. 变更说明不超过 5 条 3. 你做出的假设列表 【验收标准】 请同时给出覆盖以下情况的测试空列表、单条完整记录、缺 tags 字段、 tags 为空列表、created_at 格式非法、目标路径无写权限。这个模板里最值钱的是约束和验收标准两段。约束把它的自由发挥空间压到最小验收标准则逼着它自己想边界。实测下来加了验收标准之后生成的测试用例质量提升非常明显虽然仍然需要你补业务相关的脏数据但通用的异常路径它基本能覆盖到。4.2 三个烂提示与它们的改写烂写法问题在哪改写方向帮我写个登录功能没有技术栈、没有会话方案、没有错误处理要求说清楚框架、认证方式、失败次数限制、返回结构优化一下这段代码没有优化目标它可能给你改成完全不同的风格指定目标降低圈复杂度、减少重复查询、去掉嵌套这个报错怎么解决没贴版本、没贴堆栈、没贴上下文贴完整异常、贴相关代码、贴依赖版本、说明已经试过什么第二个坑我踩得最多。早期我总说优化一下结果它把一段直白的循环改成了一堆生成器和推导式行数少了可读性差了一大截团队评审直接打回来。后来我改成在不改变可读性的前提下把三层嵌套的 if 合并成提前返回输出质量立刻不一样。给它一个可衡量的目标比给它一个形容词有用得多。4.3 上下文怎么喂才不浪费预算几个我固定执行的小动作。第一贴代码时保留函数签名和类型注解删掉实现细节用# ... 省略标注。第二报错一定贴全包括异常类型、消息、最内层三到五帧调用栈缺一帧它就可能猜错方向。第三把项目的风格约定用两三条示例说明比如我们统一用早返回不用嵌套 if比描述半天有效。第四明确要求它先给方案再给代码尤其是改动超过两个文件的场景先看思路能省掉大量返工。第五要求它列出假设这一条极其有用——它列出的假设往往就是它理解偏差的地方你一眼就能发现。还有个小技巧如果它给的方案明显不对别急着说不对重写而是指出具体哪里不对。比如你在第三步假设了 id 唯一但实际可能重复这种针对性反馈能让它在同一轮里修正比重新开一轮效率高。5. 一条完整链路从需求到提交的实操记录下面用一个虚构的小项目走一遍完整流程项目叫模拟项目 X做的事情是批量整理图片元数据并导出。走完这一遍你能看到每个环节人该做什么、工具该做什么。5.1 第一步人先把接口和数据流定死这一步绝对不能交给工具。你要确定的是函数签名、输入数据结构、输出格式、失败策略。我一般会手写一个空壳文件把类型注解和异常约定先落下来再让工具填中间。from typing import Any def export_metadata(records: list[dict[str, Any]], path: str) - int: 把图片元数据导出为 CSV返回成功写入的行数。 约定 - records 为空时返回 0不创建文件 - 单条记录缺关键字段时跳过并计入 skipped不中断整批 - 目标路径不可写时抛出 IOError不做静默降级 raise NotImplementedError为什么坚持先定签名因为签名是人和工具之间的契约。签名一旦固定工具就只能填内部逻辑不会顺手把你的函数改成异步、不会擅自加参数、不会把返回值从 int 改成 dict。我见过太多人直接说写个导出函数结果拿回来的实现参数名、返回类型、异常策略全是它自己定的改起来比从头写还累。5.2 第二步先要测试再要实现顺序很重要。先让它依据签名和约定生成测试你审一遍测试确认覆盖了你关心的场景再让它写实现。这样做的好处是测试成了验收标准实现跑不跑得通有客观判据而不是靠看着像对。import pytest def test_empty_records_returns_zero_and_no_file(tmp_path): target tmp_path / out.csv assert export_metadata([], str(target)) 0 assert not target.exists() def test_missing_tags_is_skipped(tmp_path): records [ {id: 1, width: 100, height: 200, tags: [a], created_at: 2024-01-01T00:00:00Z}, {id: 2, width: 100, height: 200, created_at: 2024-01-01T00:00:00Z}, ] target tmp_path / out.csv assert export_metadata(records, str(target)) 1 def test_invalid_created_at_does_not_break_batch(tmp_path): records [ {id: 3, width: 1, height: 1, tags: [], created_at: not-a-date}, {id: 4, width: 1, height: 1, tags: [], created_at: 2024-01-01T00:00:00Z}, ] target tmp_path / out.csv assert export_metadata(records, str(target)) 1审测试的时候重点看两件事断言是不是真的在验证行为而不是只验证不抛异常以及有没有漏掉你真正担心的场景。上面这版是工具生成的骨架加上我补的两条原版没有非法日期不中断整批这一条而这恰恰是我最关心的。5.3 第三步跑、贴报错、迭代收敛实现拿回来先跑测试失败的输出原样贴回去别只贴一句报错了。这个阶段通常会来回两三轮每轮都要重新贴一次关键约束防止它改着改着把不引入新依赖给忘了。收敛之后我会做一次人工通读重点看三处异常处理是不是吞了错、循环边界有没有差一、资源有没有正确释放。顺带说两个涉及参数的场景这也是最容易出问题的地方。批量处理的分批大小我不会让它拍脑袋定而是自己算单条记录含元数据大约 1.5KB目标峰值内存控制在 64MB 以内理论批大小约 42000 条但下游接口有速率限制实际取 2000 条一批、并发 2 路峰值内存约 2 × 2000 × 1.5KB ≈ 6MB留足余量。这个计算过程要写进注释不然半年后没人知道 2000 是哪来的。重试策略同理基础间隔 200ms、倍数 2、最多 5 次总等待约 6.2 秒再叠加 ±20% 的随机抖动避免大批任务在同一时刻集中重试。这些数字都不能让它随便给因为它不知道你的内存预算和下游限流。5.4 第四步读 diff、重构、提交最后一步是把生成的代码当外部贡献来审。我固定看五样东西命名是否符合项目习惯、有没有多余抽象、日志和异常是否合理、注释是否与实现一致、有没有偷偷引入依赖。看完之后通常要做一次小重构把它的风格拉回团队习惯。提交信息里我会注明哪部分由工具生成、哪部分是人工修改这不是形式主义出问题时能快速定位。6. 把错误挡在合并之前三道闸门6.1 边界条件清单照着问一遍这份清单我贴在便签上每次审生成代码都过一遍。空值、空列表、零、负数、超大值、重复值、超长字符串、非法编码、时区、浮点数精度、并发重入、超时、下游返回异常结构、权限不足、磁盘写满。每一条都问一句这段代码遇到这个会怎样答不上来就补测试。其中最容易漏的是重复值和并发。生成代码默认假设输入是干净的、调用是串行的而真实系统里这两个假设几乎总是错的。我现在的习惯是只要函数涉及写操作一律补一条同一请求重复执行两次的测试看它是幂等还是会产生脏数据。6.2 类型、静态检查与测试的分工这三样东西挡的是不同的错误不能互相替代。类型检查挡的是接口层面的错配比如把可选值当必填用静态分析挡的是代码异味比如未使用的变量、过深的嵌套、可疑的异常捕获测试挡的是行为错误。生成代码在类型层面通常写得挺漂亮因为类型注解是它最擅长的模式匹配真正容易出问题的是行为层面所以测试的权重必须拉高。我的做法是要求所有新函数至少有一条正常路径和一条异常路径的测试覆盖率不追求数字但分支必须走到。CI 里把类型检查、静态分析、测试三道都设成阻断项任何一道不过就不许合并。这道防线一旦立起来工具带来的风险会被压得很低。6.3 依赖与安全它最爱在这里挖坑这是最需要警惕的一块。生成代码时它可能推荐一个不存在的包、一个早已停止维护的包、或者一个名字和常用包极其相似的包。我的固定动作是任何新增依赖先查官方仓库的最近更新时间、下载量、issue 活跃度确认不是空壳确认它在项目的版本约束内能装上确认许可证与项目兼容。三条里有一条不满足就改成用标准库自己实现。还有一类隐蔽风险是它给出的示例里带着占位密钥、示例域名、调试开关。这些直接复制进代码轻则配置错乱重则泄露信息。我的习惯是合并前全局搜一遍常见关键词password、secret、token、localhost、debug确认没有残留。7. 常见问题与排查速查表7.1 六类高频故障的现场处理第一类代码跑不通但看不出哪错。九成是版本问题。处理方式把依赖版本、完整堆栈、调用代码一起贴回去明确要求只在这个版本下给方案。第二类改一处坏一处。它在你原有代码上做局部修改时容易破坏周边的隐式约定。处理方式把修改范围圈死只贴目标函数改完自己跑全量测试。第三类越改越长。聊到后面它开始加抽象、加配置、加兼容层。处理方式直接说删掉所有非必要抽象回到最简实现或者干脆开新对话重来。第四类测试全绿但线上出错。通常是环境差异或者数据差异。处理方式把真实的一条脱敏数据贴进去让它按这条数据推演。第五类长对话后它开始忘记约束。处理方式固化结论开新对话把结论和约束一起贴进去。第六类生成的东西看起来对但读不懂。处理方式要求它逐行解释解释不通的地方就是你没想清楚的地方别急着用。7.2 一张表走完排查现象最可能的原因第一步动作报方法不存在版本错配或编造接口查该版本官方文档确认签名类型检查一片红注解与实际返回不符优先修类型别急着加any单测通过但结果不对测试断言太弱补一条验证具体数值的断言处理大批量时内存飙升一次性加载全部数据改成分批明确批大小来源偶发失败无法复现并发或时间相关加日志打出入参与时间戳提交后发现多出依赖未审查 diff回滚改为标准库实现7.3 几条写在笔记里的心得第一条永远不要相信它给的这个 API 接收三个参数除非你自己查过。第二条它写的注释比代码更不可信注释是它想象的意图代码才是实际行为。第三条让它解释自己的代码比自己读快但解释里可能夹着新的幻觉需要交叉验证。第四条同一个问题换一种问法往往能得到完全不同的方案卡住的时候不妨换个角度重问。第五条也是最值钱的一条当你发现自己在反复纠正同一个错误时问题通常不在它而在你的需求描述里有一个你没说出口的假设。把它找出来写进约束往往一次就通了。8. 团队里怎么用才不添乱8.1 把规范沉淀成常驻上下文个人用靠记忆团队用靠文档。我们会在项目根目录放一份简短的约定文件内容包括技术栈与版本、目录结构、命名习惯、异常处理策略、禁止引入的依赖类型、测试要求。每次开新对话先把这份文件的核心几行贴进去。这件事的价值不在于让工具变聪明而在于让所有人的输出风格趋同降低评审成本。8.2 评审关注点该往哪移有了工具之后评审的重点应该从语法和风格往意图和边界移。语法问题交给静态检查和格式化工具人只做机器做不了的判断这段逻辑符不符合业务规则、这个异常策略会不会掩盖真实故障、这个改动的影响范围有没有被低估。我在评审时固定会问三个问题这个函数在输入异常时行为是什么、这个改动会不会影响存量数据、这段代码如果三个月后要改改的人能不能看懂。8.3 怎么判断它真的有用别用感觉快了来判断用几个朴素指标单个需求的从开始到合并的耗时、合并后两周内的返工次数、评审时的评论条数、缺陷密度。我自己的观察是用了这套流程之后第一版代码产出时间明显下降返工次数基本持平缺陷密度略降但主要归功于测试补齐而不是生成质量。也就是说真正的收益来自更愿意写测试而不是代码写得更对。这个结论挺反直觉但很符合逻辑工具降低了写测试的门槛测试降低了缺陷率。最后分享一个小技巧。我在每次准备提交前会把它生成的代码单独打印出来读一遍不带任何上下文就当读陌生人写的代码。读的过程中一旦出现这里为什么要这么写的疑问就回去查。这个动作平均每次能揪出一到两个问题比任何自动化检查都有效——因为疑问本身就是你的经验在报警。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑