关于 old-coder-api
old-coder-api 是供 Agent 设计或审查 HTTP/JSON API 的技能,提供兼容性、授权、重试、分页和运行风险检查,并规定审查结果格式。
主要功能
- 01HTTP/JSON 契约设计指引
- 02现有 API 兼容性检查
- 03身份验证与授权关卡
- 04幂等性与分页指引
- 05API 审查结果格式
适用场景
- 01设计新的 HTTP/JSON API
- 02检查改动是否破坏兼容性
- 03审查访问控制与重试安全
- 04评估速率限制和大型集合
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ amazingang/ old-coder --skill old-coder-api安装后在 Claude Code 中输入/old-coder-api确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/breaking-changes.mdexamples.mdpatterns.md详细介绍
old-coder-api
灵感来自 Sean Goedecke 的文章 Everything I know about good API design(2025-08-24)。
这项技能涵盖 HTTP/JSON 契约和可运维性方面的考量。兼容性规则以 JSON 消费方为前提。对于 gRPC/protobuf、GraphQL、WebSockets 或其他协议,只有结合相应协议自身的兼容性规则,才能应用与传输方式无关的原则。它不能替代完整的应用安全审查。
好的 API 枯燥乏味。 对构建 API 的人来说,API 是一种产品;对使用 API 的人来说,它只不过是实现其他目标途中用到的一件工具。消费者花在琢磨你的 API、而非专注于自身目标上的每一分钟,都是浪费。一个有意思的 API 就是糟糕的 API——至少,不那么有意思的话会更好。
Agent 默认容易陷入两种失误,这项技能正是为避免这两者而设:
- 凭空设计。 本可使用枯燥、惯常的接口,却另行设计出巧妙而定制的接口。
- 破坏兼容性。 因为某个字段现在看起来更好,就重命名、重构或收紧其约束——悄无声息地破坏所有下游调用方。
与 old-coder 配合使用: 当两项技能都适用时,本技能负责
HTTP/JSON 契约,而 old-coder 负责工作流顺序、SPEC 审批、
gauntlet 和 EVIDENCE。请在 SPEC 审批前执行步骤 0 和各项关卡;将
保留的 API 约束和风险写入 SPEC,并通过 gauntlet 进行验证。对于没有实现工作的纯审查,请使用本技能的审查格式,不要凭空制造开发循环。
步骤 0 — 设计前先明确范围
在编写路由前,先明确回答以下三个问题:
| 问题 | 为什么会影响工作 |
|---|---|
| 面向公众还是内部使用? 能否为每个消费者发布代码? | 内部使用:可以接受破坏性变更,复杂的身份验证也没问题,无需考虑非工程师的易用性。面向公众:上述条件都不成立。 |
| 现有接口还是从零开始? | 现有接口 → 先运行 references/breaking-changes.md;兼容性优先于下文的所有改进。 |
| 产品的资源模型是否支持此 API? | API 设计应围绕产品的基本资源展开。如果资源模型很别扭(没有名称的状态机、仅存在于某个作业中的记录、未建模的父子关系),无论设计得多么谨慎,API 都会很别扭。请直言不讳,不要试图掩盖问题。 |
步骤 0 的诚实原则: 如果问题的根源在于底层模型,就应明确指出,并提出修正模型作为真正的解决方案。对本应直接读取的内容,硬套一个后台作业轮询接口,正是最糟糕 API 的成因之一——被 UI 隐藏的技术约束会在 API 中暴露无遗,迫使消费者理解远超其本该了解的系统细节。
各项关卡
逐一执行所有关卡。只有经过验证并通过时才使用 ✓;经过验证并确认失败时使用 ✗ + 具体修复方案;只有在关卡确实不适用时才使用 N/A + 原因;尚未验证时使用 ? + 原因。绝不能悄悄跳过。
1. 平淡易懂
合格的使用者应该无需阅读任何文档,就能猜到这个端点。
- 资源使用产品中的名词(
/issues、/projects、/users),采用复数形式且保持稳定。 - 使用标准动词和状态码,并遵循此 API 已确立的惯例。一般客户端错误使用
400;只有在内容类型和语法均有效,但其中包含的指令无法处理时,才使用422。资源不存在时使用404,请求受限时使用429。 - 使用标准字段名:
id、created_at、next_page、url。与此 API 其余部分已使用的名称保持一致——发生冲突时,内部一致性优先于外部惯例。 - 除非另有理由,否则使用 REST + JSON。成熟且内部一致的 HTTP RPC 接口也可以保持平淡易懂;不要为了追求 REST 的纯粹性而将其改名为资源路径。不要反复争论 HATEOAS 或 JSON 与其他格式之争;这并不重要。
- 任何出人意料的设计都需要附上一句书面说明。 如果写不出来,就采用平淡易懂的设计。
2. 不要破坏现有用户空间
仅适用于对现有接口的变更。完整对照表见 references/breaking-changes.md。
- 增量式变更没有问题:新增端点、新增可选参数、新增响应字段。应假设使用方会忽略未知字段。
- 移除字段、重命名字段、更改字段类型、移动字段(
user.address→user.details.address)、缩小枚举范围或收紧验证规则,都是破坏性变更。不要这样做,即使这样更整洁也不行。HTTPreferer标头的拼写有误,但它仍然存在。 - 如果确实无法避免破坏性变更:版本控制是最后的手段——参见参考文档。
3. 身份验证:让最简单的安全路径变得容易
许多服务器到服务器集成最初只是一个 curl 命令或一段 20 行的脚本。对于面向开发者的服务器到服务器 API,默认使用简单、权限范围明确且可撤销的 API 密钥。
- 对于浏览器/移动客户端、用户委托访问、高敏感性数据,或策略要求的环境,应改用 OAuth 或其他短时有效或发送方约束的流程。不要向这些客户端发放长期有效的持有者凭据。
- 对于每种凭据类型,都要定义权限范围、轮换、撤销、安全传输方式,以及在发生安全事件时识别或停用凭据的方法。
- 对于内部凭据管理,不适用此要求:使用基础设施已有的机制(mTLS、工作负载身份、服务令牌),同时仍须验证其运维控制措施。
4. 授权:强制执行谁可以对哪个资源执行什么操作
身份验证用于识别调用方;它并不授权操作。对于每个端点,都要明确参与者、操作、资源和租户边界。
- 在服务端对已解析的资源执行授权检查。未经核验,不要信任调用方提供的
tenant_id、所有者 ID、角色或权限范围。 - 对列表、搜索、批量操作、导出、嵌套资源和间接查找路径应用相同的检查;获取数据后再过滤并不能构成授权边界。
- 仅当操作有意设为匿名公开时才不适用,并用一句话说明理由。对于安全敏感的变更,除这些 API 审核门槛外,还必须进行专门的安全评审。
5. 对所有会产生实际操作的请求实现幂等性
500 错误或超时并不能告诉调用方操作是否已执行。如果没有幂等键,调用方只能在操作丢失和重复操作之间做选择。
- 对所有尚不具备幂等性、且会创建内容、触发操作或应用相对变更的操作,都应接受幂等键(请求头或参数);重复使用同一密钥时,应返回原结果,而不是再次执行操作。
- 对低风险操作,可将幂等键设为可选,因为偶尔重复操作的代价低于增加接入阻力。
- 如果重复操作不可接受——例如支付、转账、用药、不可逆的外部副作用——则必须要求幂等键或内在唯一的操作 ID,并在执行操作时以原子方式进行去重。
- 读取操作(无害)不需要;
DELETE /comments/32也不需要(ID 本身就是密钥,重试只会返回 404)。例外:不以 ID 为范围的操作,例如“删除最近的一条”。 - 存储方案见
references/patterns.md。
6. 影响范围、速率限制、紧急开关
UI 用户的操作速度受限于手速。通过 API 暴露的任何功能,都会被代码以代码的速度调用,永不停歇地循环调用,调用者可能根本没读过文档。
- 发布前:写下单个调用方在紧密的
while true循环中会给你带来多少成本。扇出请求、/index端点、批量导入,以及请求中按记录逐项处理的操作都很危险。 - 对所有操作实施速率限制,对开销大的操作设置更严格的限制。
- 返回
X-RateLimit-Remaining和Retry-After,让遵守规则的客户端能够降低请求频率——正是这些元数据,让你可以设置比原本更严格的限制。 - 为每个使用方保留一个紧急开关。你迟早会在一次由意想不到的集成引发的事件中用到它。
7. 分页
- 任何可能增长到很大的集合:始终采用基于游标的分页。
WHERE id > :cursor ORDER BY id LIMIT :n在记录数达到一百万时依然很快;OFFSET会随着每一页而变慢,之后再迁移的成本也很高。 - 永远有界的集合(用户的 API 密钥、项目的 5 个环境):使用页码或偏移量均可。
- 切勿返回无界列表。始终包含
next_page(URL 或游标),避免使用方自行计算。
8. 开销大的字段应为可选项,且默认关闭
如果某个字段需要额外的服务调用、对大表进行联接,或执行计算,就不要将它放在默认响应中。
- 通过
?include=subscription/includes[]数组控制是否返回该字段;保持默认响应开销低且成本恒定。 - 这是 GraphQL 思路中实用的 20%,却不必承担其成本。
- 除非用户提出要求,或代码库已经在使用 GraphQL,否则不要提议采用 GraphQL:非工程师的使用门槛高;由客户端任意构造的查询会使缓存更复杂,并增加边缘情况;后端也更难处理。它是最后的选择,而不是默认方案。
9. 不要泄露实现细节
站在陌生人的角度阅读响应。要正确使用它,是否必须知道你的存储方式?
- 泄露示例:要求客户端逐个遍历的
next_comment_id链;本该使用GET的操作,却采用POST /fetch_job加轮询的流程;内部枚举值;内部表 ID;页大小取决于分片布局的分页方式。 - 要么用简单的接口隐藏这些细节,要么在 PR 中明确说明相关技术债——不要让它悄无声息地进入公开契约。
刻意不设为门槛
既要防止设计不足,也要同样警惕过度设计:
- 不要一开始就构建版本管理机制。
/v1/前缀本身就是一项面向公众的产品选择,并非免费的占位符。只有当产品的兼容性策略有此要求时,才采用路径或标头版本控制;在第二个版本出现之前,不要构建多版本协商机制。 - 对于只有一个调用方且结果集有界的内部端点,不要添加
includes或游标。 分页和高开销字段门槛针对的可能是规模大或成本高的响应。对于幂等性,调用方数量并不能消除重试风险:只有在操作本身已具备幂等性,或明确接受重复效果时,才可以省略。 - 不要为了让 API 更漂亮而重写能正常工作的 API。 美观不值得以破坏兼容性为代价,也不值得耗费审查时间。
- 请记住,API 质量的影响有限。 如果产品有价值,即便 API 糟糕,人们也会集成它(Facebook、Jira)。如果产品没有价值,再漂亮的 API 也救不了它。API 质量决定的是两个大致同等产品之间的取舍;真正的问题是完全没有 API。因此:应用这些门槛,不要过度雕琢。
审查输出格式
审查而非编写时,只报告通过核实的问题,不要纠结个人偏好。对于仓库代码、规范和差异,请注明 file:line。对于仓库之外已发布的契约,请注明稳定 URL 和确切章节;代码证据必须使用不可变的提交永久链接,而非会变动的分支。如果缺少公开保证,意味着消费者无法依赖该行为;这并不能证明后端没有未公开的实现。只有审查证据支持时,才将门槛标记为 ✓。有仓库上下文时,应检查差异之外的内容,而不是把沉默视为通过。如果输入有意受限且无法获得更多证据,请使用 ? (unverified: <reason>);仅当某个门槛确实不适用时,才使用 N/A。门槛汇总评估的是正在审查的内容,而非提出修复建议后可能达到的状态。排序:先列破坏性问题,其次是安全边界,然后是事故风险,最后是易用性。
## API review: <surface>
Scope: public|internal · greenfield|existing
### Breaking changes (blocking)
- <field/endpoint> — <what breaks for a consumer doing X> — file:line
Fix: <additive alternative>
### Security boundary
- <authentication/authorization finding> — <credential or unauthorized action/resource> — file:line
### Incident risk
- <idempotency/blast-radius finding> — <the loop or retry that hurts> — file:line
### Ergonomics
- <boring/pagination/field-cost/implementation-leak finding> — file:line
### Gates: Boring <status> · Compatibility <status> · Authentication <status> · Authorization <status> · Idempotency <status> · Blast radius <status> · Pagination <status> · Expensive fields <status> · No implementation leakage <status>
如果没有任何问题通过核实,请直截了当地说明——空的审查结果也是有效结果。
参考资料
references/breaking-changes.md— 兼容性矩阵、版本管理指南、弃用流程。更改任何现有端点前请先阅读。references/patterns.md— 实现方案:幂等键、游标分页、速率限制标头、includes。references/examples.md— 三个简洁的本地示例:现有路由/规范差异、新项目提案,以及已建立的 HTTP RPC 路由。仅在需要具体校准示例时阅读。