ARTICLE DETAIL

资讯详情

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

AI 改接口最怕悄悄不兼容:用 oasdiff 在合并前查出 breaking change(附 CI 配置)

AI 改接口最怕悄悄不兼容:用 oasdiff 在合并前查出 breaking change(附 CI 配置) 目录一、哪些改动会让调用方出错常见的不兼容改动AI 为什么特别容易踩二、准备新旧两份契约旧契约AI 的四处「优化」三、用 oasdiff 对比安装查不兼容改动第四处去哪了四、接进 CI不兼容就不让合并核心就两条命令GitHub Actions 示例五、让 AI 改接口时带上约束可复制 Prompt六、不兼容改动非做不可时七、它查不出来的东西小结让 AI「把订单接口整理得规范一点」它改出来的东西往往确实更规范参数加上了必填校验金额改成字符串避免精度问题顺手删掉一个「看起来没人用」的字段。服务端的单元测试全部通过。上线之后老版本的 App 开始报错。问题不在 AI 代码写错了而在它不知道有人在用这个接口。接口一旦对外改动是否兼容比代码写得漂不漂亮重要得多。这件事靠人盯 diff 很容易漏最好交给工具在合并前自动检查。这篇用 oasdiff 实测一遍先看它能查出什么、查不出什么再把它接进 CI最后给一段约束 AI 改接口的 Prompt。一、哪些改动会让调用方出错常见的不兼容改动改动为什么会出错可选参数改成必填老客户端没传这个参数请求直接被拒请求体新增必填字段老客户端不知道这个字段也就不会传响应字段改类型客户端按原来的类型解析要么解析失败要么算错删除响应字段读这个字段的客户端拿不到值删除接口、改路径或方法调用直接 404 或 405请求参数的枚举值变少老客户端传的值不再合法反过来新增可选参数、新增响应字段、新增接口一般是安全的。例外是对未知字段直接报错的严格客户端这个要看调用方的实现。AI 为什么特别容易踩AI 改代码时看到的是服务端这一侧handler、结构体、测试。它看不到调用方也不知道哪些字段有人在读。「让代码更规范」这个目标本身就会驱动它做出上面表里的改动。二、准备新旧两份契约旧契约接口契约用 OpenAPI 描述放在仓库里比如api/openapi.yaml。下面是一个精简的订单接口openapi:3.0.3info:title:Order APIversion:1.0.0paths:/orders:get:parameters:-name:statusin:queryrequired:falseschema:type:stringresponses:200:description:okcontent:application/json:schema:type:arrayitems:$ref:#/components/schemas/Orderpost:requestBody:required:truecontent:application/json:schema:type:objectrequired:[amount]properties:amount:type:integerresponses:201:description:createdcomponents:schemas:Order:type:objectrequired:[id,amount]properties:id:type:integeramount:type:integerremark:type:stringAI 的四处「优化」模拟一次 AI 的「规范化」改动一共四处查询参数status从可选改成必填创建订单的请求体新增必填字段currency响应里的amount从整数改成字符串删掉响应里的remark字段对应的 diff -8,7 8,7 parameters: - name: status in: query - required: false required: true schema: type: string responses: -27,10 27,12 application/json: schema: type: object - required: [amount] required: [amount, currency] properties: amount: type: integer currency: type: string responses: 201: description: created -43,6 45,4 id: type: integer amount: - type: integer - remark: type: string最后一段 diff 看着有点绕删掉的是amount原来的type: integer和整个remark留下的type: string现在归amount。这正是人工 review 容易看走眼的地方。三、用 oasdiff 对比安装# Go 1.26 及以上goinstallgithub.com/oasdiff/oasdifflatest我本机是 Go 1.25go install会直接提示需要 1.26。Go 版本不够的话去 oasdiff 项目的 GitHub Releases 下载预编译包更省事macOS、Linux、Windows 都有。我用的是 v1.32.1。查不兼容改动oasdiff breaking old.yaml new.yaml输出是 3 条 error级别规则说明errorrequest-parameter-became-requiredGET /orders 的查询参数status变成了必填errorresponse-property-type-changedGET /orders 响应里items/amount的类型从 integer 变成了 stringerrornew-required-request-propertyPOST /orders 新增了必填的请求字段currency四处改动查出了三处而且每一条都写清楚了是哪个接口、哪个字段、从什么变成了什么。第四处去哪了删掉remark这一处breaking没有报。换成changelog看全部改动oasdiff changelog old.yaml new.yaml这次一共 5 条除了上面 3 条 error还多了 2 条 inforesponse-optional-property-removed从 200 响应里删掉了可选字段items/remarkapi-version-not-bumped有不兼容改动但版本号还是 1.0.0oasdiff 的逻辑是remark在契约里本来就是可选的客户端理应能处理它不存在的情况所以只算 info。道理没错但如果你的某个客户端就是在读remark删掉它照样会出问题。所以工具的默认规则不等于你的调用方实际怎么用。删字段这类改动就算工具没拦也要去确认调用方有没有在用查客户端代码或者看看各版本客户端的请求里有没有带上这个字段。前端或 SDK 和服务端在同一个仓库时我会先在 WES Code 里问一句「项目里哪些地方读了 remark 这个字段」有人在用就不删改成标 deprecated。第二条 info 也很实用发现了不兼容改动却没改版本号。这种提醒人工 review 时基本不会有人想到。四、接进 CI不兼容就不让合并核心就两条命令# 取出主干上的旧契约gitshow origin/main:api/openapi.yaml/tmp/base.yaml# 有 error 级别的不兼容改动时退出码为 1oasdiff breaking /tmp/base.yaml api/openapi.yaml --fail-on ERR--fail-on ERR是关键默认情况下就算查出了不兼容改动oasdiff 的退出码也是 0CI 不会失败加上这个参数才会返回 1。想更严格可以改成--fail-on WARN。我在本地建了个 git 仓库模拟过main 上提交旧契约feature 分支上换成新契约跑这两条命令退出码是 13 条 error 都列出来了。GitHub Actions 示例name:api-compaton:pull_requestjobs:oasdiff:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4with:fetch-depth:0# 要拿到目标分支的历史才能 git show 旧契约-uses:actions/setup-gov5with:go-version:1.26-name:Check breaking changesrun:|go install github.com/oasdiff/oasdifflatest git show origin/${{ github.base_ref }}:api/openapi.yaml /tmp/base.yaml $(go env GOPATH)/bin/oasdiff breaking /tmp/base.yaml api/openapi.yaml \ --fail-on ERR --format githubactions--format githubactions会把每条结果输出成 GitHub 的注释格式直接显示在 PR 页面上。GitLab、Jenkins 也是同样的思路取出目标分支的契约对比失败就拦。五、让 AI 改接口时带上约束可复制 Prompt你在修改一个已经有调用方在用的 HTTP 接口契约文件是 api/openapi.yaml。 硬性要求 1. 不得引入不兼容改动不能把可选参数改成必填不能删除或改名字段不能改字段类型不能新增必填的请求字段 2. 确实需要不兼容改动时先停下来告诉我原因并给出兼容方案新增字段并保留旧字段、给旧字段标 deprecated或者新增 v2 接口 3. 先改契约再改实现 4. 改完运行 oasdiff breaking 旧契约 api/openapi.yaml把输出原样贴给我第 4 条最管用让它自己跑检查把结果贴回来比事后人工 review 可靠。我在 WES Code 里改接口时用的就是这段约束改完契约让它在终端里跑一遍 oasdiff有 error 就按第 2 条改成兼容的写法再跑直到通过。六、不兼容改动非做不可时新增不删除。需要新的字段或类型就新增一个字段旧的保留并在契约里给旧字段标上deprecated: true等调用方迁移完再删。必填参数给默认值。与其把参数改成必填不如在服务端给一个合理的默认值老客户端不传也能正常工作。版本化。改动大到没法兼容时新开/v2/orders和旧接口并存一段时间。先通知再下线。列出受影响的调用方约好兼容期到期后再删旧字段或旧接口。七、它查不出来的东西契约和实现不一致。oasdiff 只看契约文件。代码改了、yaml 没改它就看不到。要么从代码自动生成契约要么加一层契约测试保证两边一致。语义变化。字段名和类型都没变但含义变了比如金额从「元」改成了「分」。这种改动工具查不出来只能靠 review 和测试。默认只算 info 或 warn 的改动。比如上面删掉可选字段。这类改动要结合调用方的实际情况决定要不要拦。小结AI 改接口时看不到调用方「规范化」改动很容易变成不兼容改动接口契约放进仓库改接口先改契约合并前用oasdiff breaking --fail-on ERR自动检查不兼容就不让合并工具没拦的删字段、语义变化要结合调用方确认文中的契约对比和检查流程是我在 WES Code 里改接口时的做法官网是 weisyn.com。你们团队是怎么管接口兼容性的靠 review、契约测试还是工具欢迎评论区聊聊。觉得有用的朋友欢迎点赞、收藏、关注后面会继续分享 AI 编程的实战经验。
返回列表