ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Prompt做代码审查与微重构:一份可复用的AI辅助审查模板

用Prompt做代码审查与微重构:一份可复用的AI辅助审查模板 1. 为什么我要把代码审查和微重构交给Prompt1.1 代码审查的痛点和微重构的真正难点先说个背景。我所在的团队每个迭代都有大量 PR 要过reviewer 时间被切得很碎大家水平也参差不齐。代码审查看起来是“找茬”其实非常消耗脑力既要盯业务逻辑对不对又要关注命名是否清晰、边界条件有没有覆盖、异常处理是否到位、有没有隐藏的性能问题还得顺便判断哪些地方可以小改一下让后面维护的人少踩坑。人的注意力是有限的连续看几十个 diff 之后很容易进入“视觉疲劳”状态这时候漏掉一个 P0 级别的逻辑漏洞一点都不奇怪。更难的是“微重构”这一步。不是说团队不知道哪里该改而是“不敢动”。代码在线上跑得好好的你让我为了“看起来更清爽”去改它万一改到一个隐蔽依赖怎么办所以在大多数团队里重构只发生在“不得不动”的时候加新需求、修线上 bug、或者被技术债逼得没办法。我也一样长期带着“能不动就不动”的心态写代码直到我把这套代码审查微重构 prompt 跑起来心态才慢慢转变。大模型在这里特别适合当“低成本、高耐心的第二双眼睛”。它不会因为连续看十段代码就烦躁也不会因为“这段代码是老同事写的”就不好意思提意见。更重要的是只要你把审查维度和重构纪律写清楚它每次给出的输出质量是稳定的。这恰好是提示词工程prompt engineering真正有价值的地方不是把问题丢给 AI 等答案而是把专家的审查经验、团队规范沉淀成一段可复用的指令让模型按你的标准工作。1.2 提示词工程在代码场景里的设计思路prompt 工程的核心其实就四件事角色、任务、输出格式、约束边界。角色决定模型用什么视角看问题。你让它当“刚入职的实习生”和“带了十年团队的技术负责人”它挑出来的毛病完全不一样。任务决定检查广度是只看逻辑问题还是连命名规范、重复代码、性能隐患一起查。输出格式决定结果能不能直接落地如果 AI 给你一堆长段落你还得自己重新整理一遍那不如不用。约束边界是防止它自由发挥比如“不许改接口”“不许新增依赖”“不要重写整个文件”。这套框架放在任何领域都通用但用在代码审查场景里格外合适因为代码审查天然有明确的标准和边界。你要是没加约束很容易出现一种情况AI 审查完以后顺手把你整个函数重写一遍然后补一句“这样更优雅”。我下运行时真的遇到过后面会细讲。2. 一套能直接抄的“代码审查微重构”Prompt模板2.1 模板全文这里直接给出我目前在使用的一套模板。它适合大多数中大型函数、类或单文件模块的审查也可以按语言和场景做小调整。复制下来以后把花括号里的内容替换成你的真实信息就行。# Role 你是一名拥有十年以上一线开发经验的资深软件工程师精通代码走查与微重构实践。 你擅长在不改变代码外部行为的前提下发现代码中的可读性、可维护性、健壮性问题 并提出小步、安全、可执行的优化方案。 # Context 我正在开发的项目是{一句话介绍项目} 代码所在的文件和模块是{文件路径/模块名} 代码用途{这段代码做了什么} 这段代码会被以下位置调用{整理已知的调用方没有就写“未知”} # Task 请你对下面给出的代码执行“代码审查 微重构建议”两个步骤。 第一步代码审查 从以下维度逐项检查不要遗漏 1. 可读性变量/函数命名是否清晰函数是否过长、嵌套是否过深注释是否必要且准确。 2. 职责单一函数是否做了不止一件事模块边界是否被破坏。 3. 重复代码是否存在可以安全抽取的重复片段。 4. 边界条件空值、空集合、极端输入、并发场景是否被正确处理。 5. 错误处理异常是否被吞掉错误信息是否有意义资源是否被正确释放。 6. 性能隐患是否有循环内重复计算、不必要的数据拷贝、明显的高复杂度操作。 7. 潜在Bug是否存在逻辑漏洞、竞态条件、状态污染等问题。 第二步微重构建议 针对你发现的问题按以下纪律给出建议 - 只做“微重构”单次改动的行数尽量控制在30行以内不改变任何外部行为。 - 不允许修改类名、方法签名、接口协议、依赖关系、业务规则。 - 不允许新增第三方依赖。 - 优先给出“低代码风险、高代码收益”的改动。 - 如果一个建议会触及多个调用方不要给换成更局部的小改动。 # Output Format 先输出总体评价不超过3点每点不超过20字。 然后输出一个 Markdown 表格列名如下 | 严重级别 | 代码位置(行号) | 问题描述 | 影响 | 微重构建议 | 严重级别使用P0必须修可能引发bug/ P1建议修影响维护/ P2可优化锦上添花。 表格每一行的“微重构建议”必须给出具体可执行的改法。对于其中最重要的2个问题 在表格后面补充“改动前 / 改动后”的代码块示意不要超过15行。 最后输出两列行动清单“改动项”和“预计影响范围”明确标识每个建议是否可能影响其他调用点。 # Constraints - 不要重写整个代码块只审查我贴出来的部分。 - 不要给与代码无关的大道理。 - 如果信息不足直接说明缺少哪些上下文不要编造。2.2 模板拆解每个模块为什么这样设计角色设定部分我特意强调“资深工程师”和“微重构实践”是为了让模型默认采用“稳妥优先”的视角。如果你让它当“刚入职的初级程序员”它很容易给你一堆正确但没用的建议比如“建议加注释”。如果你让它当“架构师”它可能张口就是“建议引入某某设计模式”完全脱离小步重构的克制感。审查维度那七项基本覆盖了日常代码审查最常犯的错误。很多人写 prompt 只写一句“帮我 review 这段代码”模型就只会泛泛而谈有时候连低级的参数空指针问题都发现不了。把维度一条一条列出来相当于给模型一张检查清单让它一项一项过覆盖面会明显提升。微重构纪律是最关键的部分。你注意看“如果一个建议会触及多个调用方不要给”这句这是我和 AI 反复对抗以后总结出来的。模型特别喜欢“抽一个公共函数然后让所有地方都调用”这种理想方案但它根本不知道这个项目里函数被谁调用贸然把建议写成“抽取公共函数”很可能把一个低风险微重构变成了全链路高风险改动。加了这条约束以后输出明显“怂”了很多也安全了很多。输出格式上P0/P1/P2 的分级标准是借鉴了故障定级思维。P0 表示不改会出问题P1 是影响长期维护P2 是可做可不做。有了分级你拿到结果以后不需要重新判断优先级直接按级别排计划即可。表格里的“代码位置(行号)”看起来很简单实际很重要它逼着模型给出精确位置而不是说一句“有一段代码”。3. 实操全过程从翻车到稳定的三版迭代3.1 拿一段真实代码测试光讲模板没意思我来演示一遍完整过程。假设我正在处理一个用户注册保存的接口服务代码长这样刻意保留了常见问题# 原始示例save_user 函数 import requests def save_user(data, notifyTrue): if data is not None: if name in data and data[name] ! : user_id data[id] if id in data else 0 cleaned { name: data[name].strip(), email: data.get(email, ).strip().lower(), age: data.get(age, 0), phone: data.get(phone, ).strip() } if cleaned[email]: if in cleaned[email]: if cleaned[age] 18: result requests.post(http://internal-api/user/save, jsoncleaned) resp result.json() if resp.get(ok): if notify: send_notify(user_id, cleaned[email]) return {success: True, id: resp.get(id)} else: return {error: save failed} else: return {error: age must be 18} else: return {error: email format invalid} else: return {error: email required} else: return {error: name is required} return {error: data required}一眼看过去至少有这些问题嵌套太深、职责不单一、email 校验只判断了一个 符号、requests 异常完全没有捕获、result.json() 没有判断响应状态码、notify 参数为 False 时没有导入任何提示却静默跳过。如果是人工审查可能要反复看到第三遍才会意识到异常处理空缺AI 则可以在第一次跑的时候把这些全部暴露出来。我把这段代码贴进上面的 prompt把 Context 里的项目信息填好第一次运行的结果让我喜忧参半。3.2 第一版输出的典型问题第一版输出确实抓到了很多问题比如 email 校验太弱、requests 没有异常捕获、嵌套过深这些都在点子上。但同时也暴露了 prompt 没有约束住的部分第一模型在结尾加了一大段“完整重构版建议”直接把整个 save_user 函数重写成了 40 行的新版本这完全违反了“不要重写整个代码块”的精神。虽然没有真正改文件但它确实提出了“直接把这段替换成”这样带有全量重写意味的建议。第二有一条 P1 建议是“建议把校验逻辑抽取到公共函数 validate_and_clean_user_data 中供后续复用”听起来很合理但实际上这个模块只有 save_user 一个调用点而且跨服务复用的时机完全未知。这种“为了未来而提前抽象”的建议恰恰是我说的“会触及多个调用方”的高风险改动应当被约束过滤掉。第三有几处行号明显对不上模型把第 3 行的代码描述成了第 6 行应该是模型在估算而不是逐行读取。问题很明显不是模板没用而是约束还不够硬。于是我做了两轮调优。3.3 两轮调优后的变化第一轮调优只改了约束部分没有动角色和任务。我在微重构纪律里增加了一条只能针对我贴出的代码片段内做局部修改禁止给出跨文件、跨模块的抽象方案同时在 Constraints 里把“不要重写整个代码块”改成了“禁止输出超过 15 行以上的替换代码”。另外加了一句如果某条建议需要改动多个调用点请不要给出改为建议我手动评估。第二轮调优我引入了 few-shot 示例。我在 prompt 末尾附了一个很小的例子展示我期望的“一个问题 一条建议”的格式比如示例输入 if len(arr) 0: return True else: return False 示例期望输出 | 严重级别 | 代码位置 | 问题描述 | 影响 | 微重构建议 | | P2 | 第1-4行 | 可以用布尔表达式替代if-else | 可读性一般 | return len(arr) 0 |这个 few-shot 非常有用模型对输出格式的理解立刻稳定了不会出现“有时给表格、有时给长段落”的漂移。调优后的输出更聚焦。它照样抓出 email 校验弱的问题但不再建议“新建一个公共类”而是说“在当前函数内增加 email 格式判断逻辑改动范围约 5 行”对嵌套过深的问题建议“使用 guard clause 提前返回并在当前函数内完成不影响函数签名”。这才是真正可以放进 PR 描述里的微重构建议。3.4 把建议变成可执行的行动计划拿到 AI 输出以后不要直接照着改。我会先做一个“收益 / 风险”排序。原则很简单P0 级问题优先改尤其是异常捕获和校验漏洞这类改动通常不影响外部行为风险低、收益最高。P1 级问题挑改动面小的先做比如“把嵌套改写成 guard clause”一次只改一个函数。P2 级问题统一记到 TODO 列表攒到下一次相关需求时顺带处理。比如前面那段 save_user 代码我的执行顺序是先给 requests.post 包一层 try / except再把 email 校验从“判断 ”改成用标准库 email-validator最后做 guard clause 重构。三步分别提交每步都有独立的测试覆盖即使某一步出了问题回滚也很容易。4. 常见问题、避坑与多场景扩展4.1 模型“幻觉”严重行号对不上怎么办这是用 AI 做代码审查时最常踩的坑。模型有时候会给出一个看起来很有道理、但代码里根本不存在的问题最常见的表现是行号估算错误。我第一次跑的时候发现模型把“缺少异常处理”挂在第 6 行实际上那段代码在第 12 行。解决思路有两个。第一在大模型回答后不要直接复制行号就提交要把行号当作线索而不是事实改代码前先定位确认。第二在 prompt 里明确要求“必须引用原始代码片段的至少 5 个连续字符作为位置锚点”比如“data.get(email, ” 这一段。这样模型必须从原文里找证据幻觉概率会明显降低。4.2 模型看不懂项目上下文给出错误判断给模型一个完全不认识的业务模块它经常会从“代码洁癖”的角度提出合并或抽象建议结果破坏业务语义。比如我见过它建议“把两个长得像的校验函数合并成一个”实际上这两个函数所在服务的租户隔离逻辑完全不同合并会导致严重数据越权。给上下文信息很关键。不需要太长几句话就行这个项目是做什么的、这个模块在什么场景下运行、它被谁调用。如果你自己也不清楚调用方那就直接在 Context 里写“未知”然后手动在 prompt 里加一句“如果信息不足不要做跨文件/跨模块的推断”。这两个小动作能挡掉大部分误判。4.3 建议越改越大从微重构变成大重构模型天然有“完美主义”倾向你让它做微重构它可能回你一个“这个类应该拆成三个文件”的方案。这不是它不听话而是“微重构”这个词对模型来说太主观了它需要更明确的边界。我在模板里把“单次改动的行数尽量控制在 30 行以内”写成了硬指标同时规定“如果建议会触及多个调用方就不要给”。这两条组合起来以后模型基本不会再给“抽取公共模块供所有服务复用”这类大工程了。如果你还遇到这类输出可以先检查自己是不是在 Context 里写了太多“这个模块未来可能会被多个团队使用”之类的话模型会顺着你的话扩展开。4.4 输出格式漂移和长度失控有些人用同一个 prompt第一次输出带 Markdown 表格第二次输出纯段落第三次直接开始聊天。这是因为你没有把输出格式钉死。最简单的办法是加一个 few-shot 示例给它看“输入长什么样、输出就应该长什么样”。其次可以在 Output Format 里加数量限制比如“P0 不超过 3 条总条数不超过 8 条”“每条建议不超过 80 字”防止模型一口气输出 20 条建议把你淹没。如果一次审查的代码超过 200 行建议拆成几个小的函数片段分别提交。大模型对超长上下文的注意力分布并不均匀中间部分很容易被“忽略”。拆成小块以后每一块都能得到相对完整的审查我自己实测下来的覆盖率比一起扔进去高不少。4.5 多语言、多场景下怎么调整审查维度模板里的七个维度是通用版遇到不同技术栈需要增删。前端组件的代码审查可以额外加几项可访问性是否有 aria 标签、状态更新是否在 effect 的依赖数组里、是否有明显的重复渲染开销、样式副作用是否可控。SQL 脚本审查可以加索引是否被隐式类型转换破坏、JOIN 是否比子查询更优、WHERE 条件是否能回表、分页是否稳定。我现在的做法是把这些维度做成“可插拔”的角色卡。基础模板里放最通用的审查项针对具体文件在 Task 里追加一段“额外检查项”。这样既保住了通用质量线又能适配不同项目的特殊要求比维护十几套完全不同的 prompt 更好管理。5. 这套Prompt的局限性与我的使用习惯这套 prompt 用了两三个月最大的感受是它适合做第一轮快速筛查不适合当最终结论。AI 能帮你把大部分常见问题暴露出来但“业务语义是否被破坏”“架构层面的抽象是否合理”这种判断仍然需要真正了解项目的人来拍板。我现在的流程是先把代码丢给 prompt 做一轮初审拿到问题清单后再人工复核一遍确认优先级最后才动手改。另外一个小习惯是我会把每次调优后的 prompt 版本存下来标上日期和改动原因。用 AI 做代码审查这件事本质上是在不断打磨自己的“审查标准”。你今天觉得合理的约束跑一个月后发现它挡住了某些场景那就删掉重调。prompt 工程本来就是一个持续迭代的过程这不单是写给模型的提示词也是你团队代码规范的具象化版本。我自己在实践中最有价值的一步是给每个审查维度都写了一句“为什么要有这一条”。比如“不允许新增第三方依赖”不只是因为组件体积更是因为在很多企业项目里引入一个新依赖需要走一堆安全审批流程一个微重构根本不该引入这种成本。当你能把每一条约束背后的因果讲清楚这个 prompt 才算是真正属于你自己的。
返回列表