
2011 年 10 月Google 工程师 Steve Yegge 在 Google 上误发布了一篇批评 Google 平台战略的内部长文。这篇文章后来被技术圈称为 “Steve Yegges Google Platform rant”是软件工程历史上被讨论最多的一篇内部吐槽。文章没有抱怨界面设计也没有讨论数据中心选址而是围绕一个非常工程化的问题展开Google 当时拥有业内最强大的内部基础设施从分布式文件系统到大规模集群调度器为什么始终没有像 Amazon 那样把这些能力沉淀为外部开发者可以依赖的平台。这篇 rant 到今天仍然有价值是因为它讲的不是某一家公司的八卦而是 API 设计、服务化架构、平台工程和团队协作方式的基本问题。很多团队在建设内部平台时犯的错误在 2011 年已经被 Steve Yegge 用很直白的语言点名过。下面先还原事件和文章核心逻辑再拆解平台、API 契约、dogfooding 三个概念然后给出一套可以用于评估自身服务化程度的验收清单最后用一个最小的 API-first 下单服务示例说明怎样把吐槽变成可落地的工程方法。1. 先还原那场争论一次误发如何变成公开案例1.1 事件背景内部长文被意外发布到 Google2011 年 10 月Steve Yegge 是 Google 的一名工程师也是当时有影响力的技术写作者长期在个人博客上讨论编程语言和软件工程。他在公司内网写下一篇关于 Google 平台战略的长文批评 Google 在对外平台建设上相比 Amazon 明显落后。文章发布时被设置为不公开但在实际操作中误发到了公开的 Google 信息流随后被大量转发。原文是一篇类似“内部宣讲”的长文不是严谨的白皮书观点带有情绪和夸张也夹杂大量个人经验。但它包含的信息量非常大他对比了 Google 和 Amazon 两家公司在“把内部能力变成外部平台”上的差距指出 Amazon 通过强制 API 化把一个电商公司变成了云计算的奠基者而 Google 虽然内部有 Borg、Bigtable、MapReduce 这类后来影响整个业界的系统却长期没有把它们变成外部开发者可以消费的服务。这一段从事件本身看是事故从技术传播的角度看反而成了一次高曝光度的架构公开课。后续很多关于“平台思维”和“API 优先”的讨论都会引用这篇文章作为起点。1.2 核心矛盾基础设施强不等于平台能力强这里要区分两个概念。基础设施强指的是团队内部有高性能的存储、计算、调度、消息等组件内部工程效率很高。平台能力强指的是外部开发者或者公司内部其他业务团队能够稳定地、自助地基于这些能力构建自己的产品。Steve Yegge 的核心观点是Google 属于前者Amazon 属于后者。Amazon 当年为了让电商业务内部不再互相写死强制所有团队之间只能通过服务接口通信。这个决定在当时看起来像管理命令实际上把公司业务切成了一组边界清晰的服务后来这些服务逐步收敛成 AWS 对外提供。而 Google 的很多内部系统在设计时只考虑“服务自己”没有把接口做成对外部开发者友好的抽象也没有形成稳定的 API 契约。于是内部很强外部很难用。这就是“基础设施强平台能力弱”的典型形态。1.3 为什么十多年后仍然值得读2011 年到现在技术栈已经变了很多但问题没有消失。今天的微服务、Service Mesh、API 网关、内部开发者平台IDP本质上都在解决同一个问题怎么让一个复杂组织里的能力被其他团队可靠地、自助地、低成本地使用。如果只把服务拆细却没有把接口契约、版本策略、权限模型和对外语义设计好得到的只是“分布式单体”而不是平台。技术选型会过时API 设计原则不会。重读这篇 rant相当于用一次真实的组织案例理解 API-first 为什么是服务化架构的前提。它不只是开发规范而是一种组织协作方式。2. 三个关键概念平台、API 契约与 dogfooding2.1 平台是“别人能在上面构建业务”的基础层先讲通俗含义。一个系统如果只能被自己的开发团队使用它叫内部工具如果其他团队或者外部开发者能稳定地、在不需要了解内部实现的情况下构建业务它才叫平台。技术定义上平台是提供一组稳定接口和运行环境让第三方在其上构建、运行和交付应用的基础设施。这里的第三方可以是公司内其他部门也可以是外部开发者。用 rant 里的对比来说Amazon 把电商内部能力 API 化之后外部开发者在 AWS 上申请计算资源、存储资源、消息队列和服务不需要知道 Amazon 内部有多少台机器、机房在哪里、调度系统叫什么名字。接口就是边界边界之外全部隐藏。容易误解的地方在于很多人认为“把系统做成通用模块”就是平台。其实模块复用解决的是代码层面的复用平台解决的是业务能力层面的复用。模块要调用方在构建期集成平台只需要调用方在运行时通过 API 消费。两者对团队的耦合程度完全不同。模块耦合在版本号上平台耦合在接口语义上。2.2 Amazon 的 API 指令用强约束逼出服务化组织Steve Yegge 在 rant 里重点讲了一个细节Amazon CEO 在 2002 年左右下达了一条内部指令要求所有团队的数据和功能必须通过服务接口暴露团队之间只能通过网络接口通信不能直接读其他团队数据库不能通过共享内存或后门链接所有接口都必须按“未来可以对外暴露”的标准设计否则会被解雇。这条指令的本质是用管理层强约束打破团队之间靠数据库共享、代码互相调用形成的隐式耦合。一旦通信方式被限制为“只能通过 API”团队就必须把边界定义清楚把数据结构、接口语义、错误处理、版本策略都显式化。没有人能悄悄修改一张表就影响全局因为别人看到的是接口不是数据库。这个约束放到今天是微服务拆分的基本原则服务之间只能通过 API 通信禁止直连数据库、禁止共享缓存、禁止私有 JAR 到处引用。如果团队没有这类硬性约束只靠大家自觉来制定边界最后一定会退回到点对点耦合。很多服务化项目失败的起点就是没有把“只能通过 API”这条规则真正落地。2.3 dogfooding 是验收机制不是口号“吃自己的狗粮”在技术圈常被说成“自家东西自家先用”。它真正的工程含义是一个平台如果连自己内部的核心业务都不愿意用就不可能有外部开发者愿意稳定使用。原因很简单。内部是离问题最近、反馈最快、业务量最真实的一批用户。如果平台能在内部业务的真实流量、真实异常、真实峰值下稳定运行外部用户面对未知场景时至少有一份经过验证的基线。反过来如果平台只是内部团队为展示而造不参与真实业务接口在边界情况下的设计缺陷就不会暴露。在工程落地时dogfooding 不是“顺便用一个接口”而是“平台团队自身就是第一个付费用户”。接口文档、错误提示、限流策略、权限申请流程都必须先被自己的真实业务使用才能迭代到可对外程度。这一点在今天的云产品里仍然成立很多云厂商要求内部业务优先使用自己的云产品保证新能力有真实用户反馈。3. 从 rant 反推一套服务化架构验收清单3.1 服务是否具备“可被外部消费”的第一印象判断一个服务是不是“平台化服务”可以问三个问题新业务团队能不能自助申请权限、查询接口文档、完成联调接口是否提供稳定的版本调用方是否需要知道服务内部的数据表结构如果三个问题的答案都不理想说明当前服务还停留在“内部接口”阶段。内部接口往往依赖调用方对自己代码的理解文档不全、版本随改随发、错误语义模糊。外部消费方一旦接入所有不明确的点都会变成工单。推荐的做法是把一个内部服务想象成要交给陌生团队使用文档里能不能仅凭接口描述调通错误码是不是有统一规范有没有沙箱环境这些是平台化的起点。平台不是把网关架起来再说的结果而是从接口设计第一天就要回答的问题。3.2 接口契约版本、兼容性与语义接口契约不是“定义几个字段”而是双方的长期约定。至少要覆盖以下内容请求和响应的数据结构字段含义和取值范围。错误码体系包括业务错误、参数错误、鉴权错误、限流错误、系统错误。版本策略例如 URL 路径带 v1、v2还是请求头带版本号破坏性变更如何发布。兼容性规则例如只允许新增字段不允许删除字段或改变已有字段语义。常见做法是用 OpenAPISwagger描述契约用契约生成文档和客户端 SDK。契约文件本身要进代码仓库、参与版本管理、接受 review。只要契约稳定实现方内部无论怎么重构调用方都不会受影响。这里有一个关键点兼容性不只是“字段还在不在”还包括语义是否变化。一个字段从“必填”改为“选填”或者从“订单金额含税”改为“订单金额不含税”都算破坏性变更。这类问题无法靠自动化工具完全发现必须由契约 review 兜底。3.3 服务治理注册发现、限流、权限、可观测性只有接口没有治理平台在真实流量下会迅速失序。服务化架构至少要具备四类能力。一是注册发现。服务实例动态变化时调用方要能通过注册中心找到可用实例而不是把 IP 写死在配置里。二是限流与配额。一个平台面向多个消费方必须能按调用方限流、按接口限流防止某个消费方的异常流量拖垮整个服务。三是权限与认证。接口要区分匿名、内部服务、外部应用、管理端等身份使用统一的鉴权机制至少要支持 API Key、内部服务账号必要时上 OAuth2。四是可观测性。每个接口都要有监控指标、访问日志、追踪信息、错误率统计。没有可观测性的平台出问题时只能靠调用方反复重试来定位。治理能力解决什么常用组件示例注册发现实例动态变化Nacos、Consul、etcd 服务框架限流配额防止单方拖垮整体Sentinel、网关限流权限认证区分身份与授权范围API Key、OAuth2、JWT可观测性定位故障和分析流量Prometheus、Jaeger、ELK组件名只作示意落地前要确认与自身技术栈的兼容性。先选稳定的核心组件再逐步扩展。3.4 团队边界API 契约比代码复用更值得作为边界微服务拆分时很多团队会把“代码能不能复用”作为边界判断标准。结果是一旦发现公共逻辑就抽一个公共模块所有服务引用同一个 JAR 或同一个 npm 包最后公共模块一变所有服务一起受影响。从 API-first 的角度看团队边界应该以“能不能独立交付和独立演进”为标准。两个服务之间如果只需要通过网络 API 交互就可以分开如果必须共享一个私有包说明边界还没设计清楚。公共逻辑确实可以放入共享 SDK但共享 SDK 要按公共契约管理版本升级要有策略不能允许每个服务随意锁定或随意升级。这一条是在组织层面复现 rant 的核心平台化的前提是团队之间通过显式接口协作而不是通过隐式实现共享协作。4. 最小案例用 API-first 把“下单能力”改造成可复用服务4.1 场景拆解假设有两套业务一套是电商小程序一套是线下门店收银系统都需要创建订单、查询物流、取消订单。用传统写法两套业务各自实现一套订单逻辑数据库各建各的表支付回调后两边各更新一份状态会出现对账不一致。平台化思路是把“订单”抽象为一个订单服务提供创建订单、查询订单、取消订单、接收支付回调四个接口。两套业务都通过 API 调用这个服务订单数据只有一份。这个例子在真实公司里对应的是一个业务能力被多个业务端复用且必须保证数据一致性。API 契约就是这些业务端之间的“宪法”。先不写实现代码先写契约这是 API-first 最核心的差别。4.2 先写 OpenAPI 契约再写实现用 OpenAPI 描述创建订单接口一个最小示例openapi: 3.0.0 info: title: Order Service version: 1.0.0 paths: /v1/orders: post: summary: 创建订单 operationId: createOrder requestBody: required: true content: application/json: schema: $ref: #/components/schemas/CreateOrderRequest responses: 201: description: 创建成功 content: application/json: schema: $ref: #/components/schemas/Order 400: description: 参数错误 content: application/json: schema: $ref: #/components/schemas/Error 429: description: 限流 content: application/json: schema: $ref: #/components/schemas/Error components: schemas: CreateOrderRequest: type: object required: - buyerId - skuId - quantity properties: buyerId: type: string description: 买家编号 skuId: type: string description: 商品编号 quantity: type: integer minimum: 1 description: 购买数量 remark: type: string description: 备注可选 Order: type: object properties: orderId: type: string status: type: string enum: - CREATED - PAID - SHIPPED - CANCELED Error: type: object properties: code: type: string message: type: string契约里值得注意的点状态用枚举而不是开放字符串避免调用方理解产生歧义错误码统一避免每个团队各自定义一套请求必填字段明确创建成功返回 201 而不是 200语义更准确429 是限流场景的统一响应。写完契约后服务端按契约实现客户端按契约生成 SDK。这样任何一端改接口定义都会在代码生成和联调阶段暴露问题而不是上线后才爆雷。4.3 内部调用与外部开放共用同一份契约平台化落地时最容易出现的问题是内部调用和外部开放各有一套接口。内部用老接口、外部用新接口两个版本逻辑漂移最后内部接口成为技术债。正确的做法是内部核心业务优先调用设计为可对外开放的同一份契约服务。即使短期内不对外接口也按外部标准设计。这样内部流量承担了 dogfooding 的职责真实业务能把错误处理、限流、并发、数据一致性问题提前暴露出来。例如电商小程序的后端服务调用订单服务时走 HTTPS API Key 业务上下文外部第三方应用未来接入时走完全相同的接口只是在网关层补充额外的认证和配额。网关可以加但不能改变服务本身的契约语义。4.4 三阶段落地路径平台化不是一蹴而就建议分三个阶段。第一阶段内部服务契约化。把现有业务改造为对内部团队开放的 API文档、版本、错误码统一内部调用全部走 API。第二阶段半开放。在 API 网关后面提供沙箱环境引入少量内部非核心业务或合作伙伴试用收集真实反馈调整契约。第三阶段对外发布。补充计费、配额、审计、SLA、公告和版本弃用策略。只有前两个阶段跑稳了对外才不至于被真实流量打崩。学习环境里可以只做第一阶段验证 API-first 流程生产环境必须把二、三阶段的治理能力补齐。5. 平台建设中的常见误区与排查清单5.1 误区一把内网接口网关化就算开放平台现象团队把已有内网接口挂到网关加一层鉴权就宣布平台上线。外部开发者接入后发现接口文档不完整错误码五花八门字段名是内部缩写接口经常随内部重构而变化。原因网关只是流量入口不是平台。平台要解决的是契约、稳定性和自助性网关解决的是路由和统一认证。接口本身的设计如果没有按外部消费标准来做加多少层代理都不能改变它的脆弱性。解决方式先按 3.2 整理的契约清单逐项补齐再开放。不要用网关替代接口设计。5.2 误区二脱离真实业务先造“通用能力”现象平台团队成立后第一件事是设计一套“通用的权限系统”“通用的消息中心”“通用的用户中心”追求大而全结果做出来没人用。原因通用能力必须在真实业务里提炼。没有具体业务方设计者无法判断边界条件、并发量、数据模型和错误语义做出来的抽象要么过度设计要么覆盖不了真实需求。解决方式平台团队要绑定至少一个核心业务方从一到两个真实场景提炼能力。先满足一个业务再复制到第二个业务。第二、三个业务带来的抽象才是可靠的。5.3 误区三只给接口不给契约、版本和弃用策略现象服务提供了接口但没有版本号调用方升级时服务端直接改字段语义旧版本下线没有任何公告和过渡期。整个调用方社区长期处在“跟着服务端改代码”的节奏里。原因平台的价值在于稳定。接口一旦被多个调用方依赖就不再只是服务端自己的代码而是一份多方契约。没有版本和兼容性策略契约形同虚设。解决方式约定至少保留一个旧版本破坏性变更必须给出迁移方案和过渡期。版本策略要写进开发规范在 Code Review 中检查。5.4 平台化改造排查清单在排查“为什么平台没人用”或者“为什么平台经常出问题”时按顺序检查检查项自查问题失败信号契约接口文档是否完整、版本是否清晰联调靠口头沟通自助性外部团队能否自助申请权限并完成联调所有接入都要找平台团队手工操作稳定性接口是否有明确的重试、超时、限流策略调用方超时后无统一处理可观测性是否有调用量、错误率、时延监控出问题靠双方对日志兼容性是否允许新增字段但不破坏旧字段服务端升级导致调用方报错流量验证内部核心业务是否实际使用该接口只有演示 Demo没有真实业务这张表可以直接拿来做团队内部的服务化答辩检查表。每次平台改造迭代后都应该把这六项重新过一遍。6. 从 rant 里带走什么给工程团队的落地建议6.1 个人项目与小型团队先做什么个人项目或三五人团队不必一开始就引入微服务和大量治理组件。先做三件事第一把项目中的核心能力设计为稳定的模块接口内部调用通过明确的函数签名或本地服务接口完成不要通过共享数据库表隐式耦合。第二如果项目需要对外提供数据能力从第一版就维护一份契约文件哪怕只是 OpenAPI 或简单的 Markdown 文档也比什么都没有强。第三强迫自己作为第一个调用方使用自己设计的接口。如果自己在使用时都觉得别扭说明接口语义有问题。6.2 中大型团队如何推进契约治理中大型团队推进 API-first建议按以下顺序先确定契约管理方式。把 OpenAPI 文件放入独立仓库服务端和客户端都从契约生成代码避免手写两套定义。再定义接口规范。统一错误码结构、时间格式、分页格式、幂等等规则。这些看起来琐碎却是多个服务能否被统一消费的基础。然后建立验收流程。新增或变更接口必须经过契约 review检查版本策略、兼容性、错误处理、安全性和可观测性配套。最后用工具落地。引入 API 文档平台、网关、契约测试把规范从文档变成自动校验项。6.3 最值得练习的一课把自己变成第一个用户整篇 rant 最值得记住的工程判断是平台不是别人造出来的词而是“自己是否愿意用”的结果。如果一个 API 连自己的真实业务都不在用它就没有被验证过没有被验证过的接口不能称为平台能力。练习的方法是每次写完一个服务接口不看隐藏的内部实现只按文档试着调用它。用另一个进程、另一个账号、另一个网络环境完全模拟陌生调用方。这个简单的习惯能暴露文档缺失、鉴权混乱、错误语义不清、版本不兼容等大多数平台问题。更进一步可以把自己的服务部署到与生产兼容的测试环境让另一个团队按文档完成一次无协助接入。全程记录被问过哪些问题、改过哪些配置、绕过哪些坑这些问题就是平台化的改进清单。延伸阅读方面可以继续研究 API 网关设计、OpenAPI 规范、内部开发者平台IDP、契约测试和服务网格相关资料。这些主题都在解决 rant 里指出的同一类问题如何让复杂系统的能力被稳定地、大规模地复用。理解了 2011 年那场争论再看这些技术方案会更清楚它们到底在解决什么。