小项目实战这个系列我之前写过一版,今天这篇算第二篇。这几个月我一直在做内部工具的开发,最深的感受是:业务逻辑反而好写,真正让人头疼的是接口设计和异常处理。接口一改,前后端联调就乱;异常没兜住,半夜报警电话就会打过来。后来我试着把这两个环节交给 AI 来补齐——不是让 AI 直接写完整业务代码,而是让它先出接口设计稿、再枚举异常场景、最后生成处理框架。这一轮试下来,效率提升非常明显,但也踩了不少坑,所以把完整过程整理出来,给正在做后端接口、全栈开发或独立项目的朋友参考。
适合读这篇文章的人,大概有几类:一是接口文档总是被前端吐槽“字段又改了”的后端开发;二是项目里异常处理写得比较随意,全靠 try-catch 硬扛的同学;三是想试试 AI 辅助编程,但不知道从哪里切入的开发者。我尽量把提示词、对话思路、落地代码和排查经验都写清楚,让你可以直接照着用。
1. 项目实战背景与思路拆解
1.1 为什么接口设计和异常处理最容易翻车
接口设计表面上是“定义几个字段、约定几个状态码”,但实际做起来牵扯的事情特别多。一个订单接口,前端要看字段名、类型、嵌套结构;后端要看校验规则、幂等策略、事务边界;测试要看异常分支、状态码、错误信息格式。任何一方理解不一致,联调阶段就开始互相甩锅。
更麻烦的是,异常处理很少有人在一开始就认真设计。大多数项目的常态是:遇到参数错误就返回 400,遇到业务错误就返回 500,遇到数据库异常直接堆栈打到日志里。一次两次没问题,等到接口被其他系统调用、或者对接小程序和 App 时,才发现错误码不统一、请求 ID 没法串联、上游根本不知道该怎么处理失败。
我用 AI 补齐这两个环节,核心思路是:把 AI 当成一个“随叫随到的资深同事”,让它先输出方案,我再做取舍。它不会因为嫌麻烦而漏掉边界,也不会因为心情不好拒绝评审,这对小项目来说非常实用。
1.2 AI 能帮上哪些忙,哪些忙帮不上
先说 AI 擅长的部分。
第一,AI 很擅长从模糊需求里生成结构化内容。你给它一句话“需要一个创建订单的接口”,它能补齐请求参数、响应结构、错误码、状态码,而且格式比你随手写的 Notes 规整很多。第二,AI 能快速枚举异常场景。人很容易漏掉“库存扣了但订单没创建成功”“重复点击导致创建了两单”这类情况,AI 在提示词引导下反而能列出完整的清单。第三,AI 可以保持风格统一。同一个项目里如果多个接口都由 AI 辅助生成,字段命名、错误码格式、返回结构很容易保持一致。
但 AI 也有明显帮不上忙的地方。它不了解你的业务全貌,不知道哪些异常需要发送报警、哪些只需要记录日志;它也不清楚你们团队的代码规范,可能生成出跟现有代码风格完全不搭的类名和函数名。所以,AI 适合做“初稿生成”和“查漏补缺”,人工必须做“决策和把关”。这个定位摆正了,后面基本不会翻大车。
1.3 这次实战的准备工作
为了把过程讲清楚,我挑了一个最常见的场景:创建订单接口。技术栈选了 FastAPI + MySQL,原因是上手快、适合小项目,而且 AI 对 FastAPI 的掌握程度非常高。不管你是用 Spring Boot、Gin 还是 Express,思路完全一样,代码参考一下即可。
准备工具方面,我用了两个 AI 入口:一个是网页版的对话助手,用来做接口设计和异常场景的头脑风暴;另一个是 IDE 里的 AI 编程插件,用来在写代码时快速补全异常处理逻辑。实测下来,两者各有优势:网页版适合长上下文、多轮推演;IDE 插件适合在真实代码文件里直接生成代码片段,但上下文不够时容易“断片”。
2. 用 AI 补齐接口设计的关键步骤
2.1 提示词是第一步:把模糊需求翻译成接口需求
很多人用 AI 生成接口设计,效果不好,原因通常是提示词太简单。你只写“帮我设计一个订单接口”,AI 只能给一个泛泛的 RESTful 模板,跟你的业务完全对不上。正确做法是给足上下文,让 AI 知道你有哪些约束和偏好。
我常用的提示词模板是这样的:
你是一名资深后端工程师。请帮我设计一个“创建订单”接口。 技术约束: - 使用 FastAPI + MySQL - 返回结构统一为 { code, message, data, request_id } - 接口风格遵循 RESTful - 需要支持幂等键,防止重复下单 - 商品库存可能不足,需要明确处理方式 请输出: 1. 接口路径、请求方法、请求头、请求体 2. 正常响应的完整 JSON 示例 3. 所有可能出现的异常场景清单 4. 每个异常场景对应的 HTTP 状态码和业务 code 5. 接口实现的核心逻辑顺序这样写,AI 会把你当成一个具体的项目角色,而不是在回答一道概念题。关键词“技术约束”很重要,它限制了 AI 的发挥空间;“请输出”后面的结构要求,让结果直接可用。
2.2 字段、状态码、错误码的统一规范
接口设计最怕前后端各玩各的。同一个错误,前端只知道“失败了”,后端却分不清是参数问题、权限问题还是下游服务挂了。所以我在跟 AI 对话时,要求它先把错误码规范表做出来。
以订单接口为例,AI 给我的输出被我整理成了一张表:
| 业务 code | HTTP 状态码 | 场景说明 | 前端处理建议 |
|---|---|---|---|
| 0 | 200 | 成功 | 正常处理数据 |
| 40001 | 400 | 缺少必填参数 | 提示用户补全信息 |
| 40002 | 400 | 商品数量非法 | 提示用户修改数量 |
| 40003 | 422 | 幂等键缺失或格式错误 | 提示刷新重试或联系客服 |
| 40301 | 403 | 用户无权操作 | 跳转登录或提示无权限 |
| 40401 | 404 | 商品不存在或已下架 | 提示商品失效 |
| 40901 | 409 | 重复提交订单 | 提示订单已提交,请勿重复操作 |
| 50001 | 500 | 库存扣减失败 | 提示稍后重试 |
| 50002 | 503 | 下游支付/库存服务不可用 | 提示网络繁忙,稍后再试 |
这张表直接扔给前端,联调效率高很多。我后来发现,让 AI 把“前端处理建议”也写出来,是避免双方扯皮的关键。因为错误码本质上是给调用方看的,不是给自己看的。
2.3 让 AI 补全边界场景:空值、并发、幂等
接口设计里最容易漏的不是主流程,而是边界。我让 AI 专门针对边界场景做了一轮补充,它给出了几个我确实会忽略的点:
- 请求体里所有字段的边界:字符串长度、数字范围、枚举值范围
- 幂等键为空时怎么办?是拒绝还是自动生成?
- 同一用户同时提交两个相同请求,数据库层面如何防重?
- 商品库存刚好只剩 1 件,两个请求同时扣减,怎么保证不超卖?
- 上游库存服务超时,是直接返回失败还是先创建订单再异步处理?
这几个问题,如果靠自己列,大概率要写到一半才想起来。用户跟你说“帮我设计接口”,实际上他要的是一个能扛住真实流量和脏数据的接口。AI 在边界场景上的枚举能力,比人脑稳定,这是它最值钱的地方。
3. 异常处理:AI 最容易出彩也最容易翻车的地方
3.1 先给 AI 明确异常分类
异常处理如果一上来就让 AI“写个全局异常处理”,它通常会给你一个堆满各种异常类的代码,看着很专业,实则过度设计。我的做法是先定义异常分类,再让 AI 填充方案。
我常用的分类很简单:
- 参数异常:请求数据不合法,通常由前端校验兜底,但后端必须再校验一次
- 业务异常:符合参数规则,但业务上不允许,比如余额不足、库存不足、重复下单
- 系统异常:数据库连接失败、缓存故障、下游服务超时等
- 第三方异常:调外部 API 时的网络错误、鉴权失败、限流
给 AI 的提示词会明确要求:
请基于以下异常分类设计订单接口的异常处理: 1. 参数异常 -> 返回 4xx,业务 code 以 400 开头 2. 业务异常 -> 返回 4xx,业务 code 以 409 或 403 开头 3. 系统异常 -> 返回 5xx,业务 code 以 500 开头 4. 所有异常响应体保持统一格式 { code, message, data, request_id }加了分类之后,AI 生成的异常处理结构清晰多了,日志、告警、返回体都能各归其位。
3.2 用 AI 生成异常处理框架和代码
异常处理代码不需要每个接口写一遍,用全局异常处理器更好。AI 很擅长生成这种重复性较高的模板代码。我用的是 FastAPI,让 AI 生成的框架大概是这样的:
from fastapi import FastAPI, Request from fastapi.responses import JSONResponse class BizException(Exception): def __init__(self, code: int, message: str, http_status: int = 400): self.code = code self.message = message self.http_status = http_status class SystemException(Exception): def __init__(self, code: int, message: str, http_status: int = 500): self.code = code self.message = message self.http_status = http_status app = FastAPI() @app.exception_handler(BizException) async def biz_exception_handler(request: Request, exc: BizException): return JSONResponse( status_code=exc.http_status, content={ "code": exc.code, "message": exc.message, "data": None, "request_id": request.headers.get("X-Request-ID", ""), }, ) @app.exception_handler(SystemException) async def system_exception_handler(request: Request, exc: SystemException): return JSONResponse( status_code=exc.http_status, content={ "code": exc.code, "message": exc.message, "data": None, "request_id": request.headers.get("X-Request-ID", ""), }, )这段代码本身不复杂,但 AI 生成的“完整度”比我自己手写快。它还会主动提醒我:request_id 应该从中间件生成,而不是依赖前端传入。这属于很好的附加建议。
3.3 人工 review 要盯这五个点
AI 生成的异常处理框架,我不会直接照搬。每次人工 review,我重点看五个地方:
- 异常信息是否泄露内部细节。比如“MySQL connection failed”绝对不能直接返回给前端,必须统一改成“服务繁忙”之类的文案,详细错误只进日志。
- 有没有把业务异常和系统异常混在一个类里。混用会导致前端无法判断到底要不要重试。
- 日志是否打印了完整的堆栈和请求 ID。没有请求 ID 的日志,排查问题时要命。
- 是否所有入口都覆盖了。比如文件上传、定时任务、消息队列消费,这些地方不一定走 HTTP 接口,全局异常处理器管不到。
- 异步任务里的异常是否单独处理。FastAPI 的 BackgroundTasks 抛了异常不会触发全局异常处理器,必须自己 try-catch。
3.4 让 AI 写异常处理的验证用例
异常处理写完了,怎么证明它有效?我让 AI 帮我生成了订单接口异常路径的测试用例。AI 给出的用例包括:
def test_create_order_product_not_found(client): response = client.post("/api/v1/orders", json={...}) assert response.status_code == 404 assert response.json()["code"] == 40401 assert response.json()["message"] == "商品不存在或已下架"这类用例的好处是能把异常场景变成可回归的检查点。下次谁改坏了,跑一遍测试就知道。AI 生成测试用例时,我会提示“覆盖所有错误码表里的场景”,它就能自动补齐大部分。
4. 实操过程:一个订单接口的完整实战
4.1 业务需求与约束定义
我把这次实战的场景定义为:用户提交订单,商品库存扣减,同时记录订单信息。项目约束如下:
- 用户 ID 和商品 ID 由调用方传入,先做存在性校验
- 每个请求必须带幂等键 Idempotency-Key,防止重复下单
- 扣减库存使用 Redis + MySQL 双重校验,避免超卖
- 返回结构统一为 { code, message, data, request_id }
- 所有异常都走全局异常处理器
在实际项目中,这些约束可能散落在需求文档、会议记录和代码注释里。我的做法是先人工整理成一段完整描述,再丢给 AI。这一步很重要,AI 不能替你做需求分析,但能把你的需求分析翻译成合格的技术方案。
4.2 第一轮对话:生成接口设计初稿
我把约束描述发给 AI,要求输出接口文档。AI 给的结果非常规整,关键内容如下:
POST /api/v1/orders 请求头: - Content-Type: application/json - X-Request-ID: 可选,建议由网关生成 - Idempotency-Key: 必填,用于幂等控制 请求体: { "user_id": "user_123", "product_id": "product_456", "quantity": 2, "coupon_id": null } 响应体(成功): { "code": 0, "message": "success", "data": { "order_id": "order_789", "status": "CREATED", "total_amount": 199.00 }, "request_id": "req_abc123" }看到这个初稿,我会重点检查几个地方:幂等键到底放 header 还是 body?AI 放 header,这个可以。但 AI 没有定义幂等键过期时间,也没有说明重复请求时返回什么——这些需要第二轮对话补齐。第一次生成的结果,通常只能当草稿,不能直接用于开发。
4.3 第二轮对话:补齐异常处理
接着我让 AI 基于错误码表,补齐异常处理策略。AI 输出的内容中,最有价值的是“重复提交”的处理方案:
当 Idempotency-Key 已存在且对应订单状态为“已创建”时,接口应返回第一次创建的订单数据,HTTP 状态码可以是 200,也可以返回 409。具体取决于业务方是否需要区分“新创建”和“重复请求”。如果前端只关心订单是否创建成功,建议直接返回 200 和原始订单数据。
这个建议让我少踩了一个大坑。很多新人在做幂等的时候,直接返回“重复请求”错误,结果用户在页面上多点了一次提交,看到的不是订单成功,而是一条错误提示。AI 能从调用方角度分析,这点很加分。
4.4 落地成代码并验证
接口设计和异常处理确定后,我让 IDE 插件基于上述设计生成核心代码。关键逻辑长这样:
@app.post("/api/v1/orders") async def create_order(request: Request, body: OrderCreateRequest): request_id = request.headers.get("X-Request-ID", "") idempotency_key = request.headers.get("Idempotency-Key", "") if not idempotency_key: raise BizException(code=40003, message="幂等键缺失", http_status=422) existed_order = await order_service.get_by_idempotency_key(idempotency_key) if existed_order: return {code: 0, message: "success", data: existed_order, request_id: request_id} user = await user_service.get_by_id(body.user_id) if not user: raise BizException(code=40401, message="用户不存在", http_status=404) product = await product_service.get_by_id(body.product_id) if not product or product.status != "ON_SALE": raise BizException(code=40401, message="商品不存在或已下架", http_status=404) if body.quantity <= 0 or body.quantity > product.stock: raise BizException(code=40002, message="商品数量非法", http_status=400) try: order = await order_service.create_order(user, product, body.quantity, idempotency_key) except InventoryShortageException: raise BizException(code=50001, message="库存扣减失败", http_status=500) except Exception as e: logger.error(f"create_order failed request_id={request_id}", exc_info=e) raise SystemException(code=50002, message="服务繁忙", http_status=503) return {code: 0, message: "success", data: order, request_id: request_id}这里有个细节:库存不足在业务上通常不是服务器错误,但“扣减库存失败”也可能代表系统数据不一致,所以 code 用 50001、HTTP 状态码用 500。具体怎么定,得看业务方怎么理解。AI 生成代码时会给你默认值,但最终状态码含义必须人工确认。
验证时我用 curl 模拟了四种情况:
- 正常创建订单
- 不带幂等键直接请求,返回 422 和 code 40003
- 商品 ID 不存在,返回 404 和 code 40401
- 带相同幂等键重复请求,第二次返回第一次的订单数据
全部符合预期。整个流程从需求到代码验证,用时比我以前手写至少省了半天。
5. 常见问题与排查技巧实录
5.1 AI 生成接口时的“幻觉”和纠偏方法
AI 有一个很典型的问题:它会一本正经地生成一个看起来合理但根本不存在的字段或接口。比如我让它设计订单接口,它可能会自己加一个payment_method,但项目里根本没有支付模块。这种幻觉在小项目里尤其容易混过去,因为看起来太合理了。
纠偏方法就一个:逐字段 review。把 AI 输出的所有字段、状态码、方法名跟真实业务对照一遍,凡是业务里不存在的概念,要么删除,要么标注“未确认”。千万不要因为 AI 给你补齐了就觉得它一定对。
5.2 异常处理过度设计的判断标准
AI 还容易把异常处理写得过度复杂。它会给你设计一个ExceptionHandlerFactory,再配一堆AbstractException、ErrorCodeEnum,小项目根本用不上。判断标准很简单:
- 异常分类是否超过三到四大类?
- 是不是每个异常都要走一个单独的处理器?
- 代码里是否出现了大量“未来可能用到”的抽象层?
如果答案都是“是”,建议让 AI 简化。小项目的异常处理核心就两件事:把状态码和错误消息正确返回给调用方,把内部错误细节留到日志里。其他都是额外负担。
5.3 让 AI 输出更稳定的几个小技巧
我试了很多种提示词写法,总结下来,这四个技巧对输出稳定性帮助最大:
- 在提示词里给一个“坏例子”和“好例子”。AI 看过样例后,输出风格会明显向样例靠拢。
- 要求 AI 分步骤输出,不要一次给完整代码。先出接口文档,再出异常场景,最后出代码,每个阶段可以单独审阅。
- 每次对话只聚焦一个目标。接口设计和异常处理虽然相关,但最好分两轮对话,否则 AI 容易把两者揉在一起,重点不突出。
- 如果 AI 给的结果不满意,不要直接放弃,而是追问“请基于你的方案,补充我遗漏的场景”。多轮追问的效果比重新生成好得多。
5.4 问题排查速查表
我在实战中总结了一张排查清单,每次 AI 生成的内容有问题,我都会按这个顺序检查:
| 问题现象 | 排查方向 | 处理建议 |
|---|---|---|
| 接口字段缺失 | 业务上下文是否给全 | 补充用户、商品、订单字段说明 |
| 错误码不统一 | 有没有先定错误码规范 | 先要求 AI 输出错误码表 |
| 异常处理全是 try-catch | 没有定义全局异常策略 | 要求 AI 设计全局异常处理器 |
| AI 生成了不存在的模块 | 上下文里没有明确模块边界 | 限制技术栈和业务范围 |
| 幂等逻辑太弱 | 没有强调重复提交场景 | 单独围绕幂等做一轮追问 |
| 代码与现有风格不一致 | 没有给出代码风格样例 | 在提示词中贴一段现有代码 |
这张表我直接放在项目笔记里,每次用 AI 辅助设计接口都会翻出来对照。
我个人在实际操作中的体会是:AI 补齐接口设计和异常处理,最高效的用法不是当“代码生成器”,而是当“设计评审脑暴对象”。它可以帮你把脑子里模糊的想法变成一个看得见、摸得着的初稿,也能在边界场景上查漏补缺。但最后的责任始终在自己身上——接口字段合不合理,异常返回会不会误导调用方,这些都需要人工亲自定夺。如果你也准备在小项目里用 AI 做类似的事情,建议先从本周正在写的接口开始,把提示词、错误码表、异常框架跑一遍,然后再决定哪些部分可以放心交给 AI。等你用顺手了会发现,真正省下来的不是敲代码的时间,而是反复沟通需求和排查线上异常的时间。