接口最难的部分往往不是把它造出来,而是在它已经被许多客户端依赖之后,还能继续修改它。每个发出去的字段、每个被假定的形状,都变成了别人代码里的隐含约定。设计经得起变化的 JSON 接口,本质上是在第一天就为日后的演进留出余地,从而既能往前走,又不至于打碎已经在用它的人。

把每个响应都看成一份公开契约

一旦客户端开始解析你的 JSON,字段名、类型和结构就成了契约的一部分。即便文档里没写,客户端也会对它们形成依赖。因此在新增或暴露字段时要谨慎:你删得越多越随意,对方就越容易崩。把响应当成承诺,而不是随手可改的内部数据结构。

一个有用的心态是:新增通常是安全的,删除和改名通常是危险的。围绕这条原则去设计,能省下大量后续的兼容性麻烦。

新增字段要兼容,删除字段要克制

给响应加一个新字段,通常不会伤害老客户端,因为它们会忽略不认识的键。这让"扩展"成为最安全的演进方式。反过来,删除字段、改字段名、或改变字段类型,都可能让正在读取它的客户端报错或行为异常。

当你确实需要废弃某个字段时,先标记为弃用、保留一段过渡期,再观察是否还有人依赖它,最后才真正移除。让消费方有时间跟上,是负责任接口的基本素养。

对可空与缺失保持明确

JSON 里"字段不存在"和"字段值为 null"是两件不同的事,客户端对它们的处理也可能不同。如果你的接口在两者之间摇摆不定,客户端就得写一堆防御性判断。明确约定:某个字段是永远存在但可能为 null,还是可能整个缺失,并在文档里说清楚。

同样,空数组和缺失数组也应当一致对待。前后一致的"空表示"能让客户端代码大幅简化,也减少边界情况引发的 bug。

谨慎使用枚举值

用字符串表示状态或类型很自然,比如 "pending""active"。但要记住,你日后很可能会新增枚举值。如果客户端写死了对全部取值的穷举处理,遇到新值就可能崩溃。设计接口时,应当鼓励客户端对未知枚举值采取宽容的兜底策略。

在文档里说明"未来可能新增取值,请优雅处理未知值",能把这条隐含期望变成显式约定,避免每次扩展枚举都引发连锁故障。

版本化是手段,不是目的

当变更确实会破坏兼容时,版本化提供了一条退路:在 URL 路径、请求头或媒体类型里标明版本,让新旧客户端各走各的逻辑。但版本化也有成本——你得同时维护多套行为。因此它应当是应对破坏性变更的最后手段,而不是替代谨慎设计的捷径。

很多变更其实可以通过"只新增、不破坏"来实现,根本不需要升大版本。把版本号留给那些真正无法兼容的重构,能让接口的演进更平滑。

用文档和校验固化约定

无模式的灵活是 JSON 的优点,但接口需要的是可预期。借助 JSON Schema、OpenAPI 或类型定义,把字段的类型、必填性、取值范围写下来,既给消费方明确指引,也给自己一道防回归的护栏。规范和校验把隐性契约变成可执行的检查。

归根结底,经得起变化的接口靠的是纪律:新增优先于破坏,明确优先于含糊,宽容优先于苛刻。这些习惯让你的接口能在多年演进中持续服务,而不必每次改动都心惊胆战。