在「编程神课」这个序列里,大部分传奇都是演讲者用惊艳的演示征服观众。而这一次反过来了:没有演示,没有特效,一个人、一页页幻灯片、一小时,讲的全是“怎么把接口设计对”——却成为无数工程师书单里绕不开的一课。
这就是 Joshua Bloch(约书亚·布洛克)2007 年前后在 Google Tech Talks 上做的《How To Design A Good API and Why it Matters》(如何设计一个好的 API,以及它为什么重要)。Bloch 不是普通的技术布道者:他在 Sun 主导设计了 Java 集合框架,写了《Effective Java》,后来任 Google 首席 Java 架构师。换句话说,这场讲座不是“我认为接口该怎么设计”,而是一个亲手设计过全球使用量最大接口库之一的人,交代自己踩过的坑和总结出的军规。
为什么 2026 年还要读它?因为今天的“接口”比十七年前多了一个数量级:你写的每一个 SDK、每一个开放平台插件、每一个给 AI 代理调用的工具函数,都是别人要“住进去”的房子。这个生态里每天生产大量一次性接口,也每天产生大量因为接口设计失误而锁死团队的工程债。这场讲座给出的不是时髦技巧,而是几乎不过时的底层判断标准——这正是它十七年不过气的原因。
| 项目 | 内容 |
|---|---|
| 视频标题 | How To Design A Good API and Why it Matters(如何设计一个好的API,以及它为什么重要) |
| 频道 | Google Tech Talks(谷歌官方技术讲座系列,据频道页面) |
| 演讲者 | Joshua Bloch,时任Google首席Java架构师,《Effective Java》作者 |
| 发布时间 | 2007年前后上传,长期被工程博客与高校课程引用(具体日期以频道页面为准) |
| 时长 | 约1小时(含问答环节) |
| 播放量 | 百万级(据频道页面,长期位列API设计类观看最多的讲座之一,具体数字以页面为准) |
| 核心主题 | API设计的判断标准、设计流程与演化策略 |
讲座的第一个转折点,是 Bloch 把“接口”从技术词汇挪进了商业词汇:API 就是产品。使用你模块的其他团队是你的客户,模块之间的边界就是市场。一旦这个视角立住,后面的所有结论都会变得自然——没有哪个产品经理敢说“先上线,不好用下个版本再改”,因为 API 的“下架成本”高得吓人。
由此引出全场最著名的一句话(大意):“公开 API 就像钻石,是永恒的。你必须努力把它打磨好,否则你会后悔。” 在 2007 年,这句话还带有一些修辞色彩;到今天,它几乎是写实——想想那些十几年不敢动一个字段名字的开放平台,想想有多少团队被早期一个仓促暴露的方法逼着写十年兼容代码。Bloch 补充的另一半理由同样锋利:代码被读的次数远多于被写。接口写完的那一刻只是一次性成本,而它将被成千上万开发者反复阅读、调用、猜测。一次省事的设计,会被成千上万倍地“复读”。
紧接着,他给出了整场讲座的“验收标准”——什么是好 API。归纳起来是四张面孔:
他特别强调“恰到好处”:API 的能力要匹配目标受众,宁可做 20% 的功能让 90% 的人用得漂亮,也不要做 100% 的功能让所有人都在文档里迷路。这条标准直接对冲了一种工程师本能——“多暴露几个方法总没坏处”。Bloch 的回答很干脆:每一个多余的方法都是未来要背的债,因为加很容易,删几乎不可能。
讲座中段进入方法论,而方法论的第一条就足够反直觉:设计 API 之前,先写使用它的代码。不是先定类图、先画架构,而是假装接口已经存在,直接写调用方的代码——先写需求,再写接口,最后才写实现。如果调用代码写起来别扭、啰嗦、需要绕弯,那就是接口本身的错,回去重画。
这套流程在今天有个更流行的名字——“以调用方视角做设计”,配合测试先行的开发方式几乎是标配。但 Bloch 2007 年就把要点讲透了:接口的最终评审标准不是实现多优雅,而是被使用时多自然。
流程上他还甩出那句被引用最多的军规:“存疑时,就不要放进去”(When in doubt, leave it out)。理由前面说过:你永远可以往 API 里加东西,但几乎永远删不掉。与之配套的是“最小可见性”原则——每一个类、每一个方法,都从最严格的可见级别开始(能 private 就不 public),暴露出去的每一个符号都是承诺。实现细节更要藏死:返回内部可变对象、暴露具体实现类,都会让调用方在不知不觉中依赖你的“内部布局”,从此你连重构的自由都没有了。
讲座最精彩的部分,是 Bloch 拿自己参与过的 JDK 开刀。这几个案例都属于“设计时完全合理、事后看全是坑”的典型,值得每个写接口的人引以为戒。
坑一:月份从 0 开始。 Java 早期 Date 类的月份参数是 0 到 11,一月是 0。后来的 Calendar 类沿袭了这个决定——Calendar.JANUARY 的值就是 0。这个设计的目的据称是方便做数组下标和数值运算,但对人类使用者来说,它是几百万人踩过的经典大坑。更深远的影响在别处:JavaScript 的 Date 直接照抄了 Java 1.0 的 java.util.Date——1995 年 Brendan Eich 用十天赶工 JavaScript 时,日期对象几乎原样移植,于是“月份从 0 开始”这个决定,就这样从 Java 渗进了全世界最大的浏览器生态,折磨前端开发者至今。一个接口的坏决定,可以跨语言、跨世纪地传染。
坑二:用继承暴露一切。 java.util.Stack 继承自 Vector,于是栈对象身上挂满了 insertElementAt 这类破坏栈语义的方法——你可以在栈底插队。Properties 继承自 Hashtable,理论上能塞进非字符串的键值。这两个设计让“是一个(is-a)”的继承语法绑架了“有什么行为”的语义承诺,也成了《Effective Java》里“组合优于继承”最有力的论据来源。
坑三:equals 里藏着网络请求。 java.net.URL 的 equals 方法比较主机名时,可能触发 DNS 解析——也就是说,两个对象判等这个看似纯内存的操作,可能要发起一次网络查询。把它放进 HashMap、放进循环里比较,程序就会莫名其妙地卡住。接口的抽象泄漏到此地步:你承诺了的操作复杂度,和实际执行的复杂度完全是两回事。
这三类坑指向同一条原则:“难误用”必须作为设计目标,而不是期望用户自律。 数字常量换枚举、布尔参数换语义化的方法名、能封死的可见性绝不敞开——本质都是在把“用错的可能性”从系统里挤出去。
讲座收尾部分回到一个看起来最朴素的议题:命名和文档。Bloch 的说法是,一个 API 就是一门小语言——程序员读你的接口,就像读一门新语言的课文。所以命名必须遵循平台惯例、保持一致性,让使用者能从旧知识外推到新接口;文档不是附属品,而是 API 的另一半本体,“没有文档的接口等于不存在”。他甚至建议设计者多去读别人的优秀接口——模仿一门好语言,胜过发明一门坏语言。
关于性能,他的立场克制而清醒:不要为了眼前性能把接口形状扭曲(比如为了让调用方少一次分配而暴露内部数组),因为性能问题通常可以在实现层面修,而接口的坏形状修不了。但他也留了余地:如果性能是核心需求,就要让接口形状贴合数据本身——这半句话在后来“面向数据设计”的语境里,反而越来越有分量。
第一,这场讲座有真实的工程实践托底。 Bloch 在 Sun 领导设计的 Java 集合框架(1998 年随 JDK 1.2 发布)是全世界调用最频繁的接口库之一;他的《Effective Java》(2001 年初版,2018 年出第三版)被公认为 Java 工程的事实标准,书中“最小化类和成员的可访问性”“组合优于继承”“用工厂方法代替构造器”等条目,正是这场讲座观点的书面完全体。讲座里痛陈的 Stack/Properties 继承问题,书中也有正式的案例讨论。
第二,“实现自由会被夺走”这件事,后来被总结成了定律。 谷歌工程师 Hyrum Wright 提出的 Hyrum 定律:当 API 的用户足够多时,你在契约里承诺什么已经不重要——系统的所有可观察行为,都会被某些用户依赖。 这条定律后来写进了谷歌 2020 年的工程方法论著作《Software Engineering at Google》(O'Reilly 出版,官网提供免费全文),成为“接口演化”议题的标准引用。它其实是 Bloch 那句“加得进、删不掉”的定量版本:你以为删掉的只是实现细节,实际上删掉的是别人的生产系统。
第三,“难误用”并非个人趣味,有可用性研究背书。 人机交互领域的“认知维度”框架(Thomas Green 等人提出)在 2000 年代被微软研究员 Steven Clarke 等人引入 API 可用性研究,用实验方法测量开发者使用接口时的认知负担——“错误可见性”“隐藏的依赖”这些维度,与 Bloch 的实战经验高度吻合。实战直觉与学术测量在同一个结论上会师,这是这场讲座最硬的底气。
第四,反面教材的传染性有真实历史记录。 JavaScript Date 抄自 java.util.Date 这段渊源,在多份 JavaScript 口述史和 MDN 社区资料中都有记载;而“操作系统为旧软件的坏行为打补丁”的文化,微软传奇工程师 Raymond Chen 在博客 The Old New Thing 上记录过大量实例——最著名的是 Windows 95 专门为《模拟城市》加了特例兼容代码,因为游戏内存释放后还在使用。接口兼容从来不只是技术问题,而是生态的集体记忆。
经住检验的部分。 “存疑就不加”与“最小可见性”几乎无可争议——十七年过去,开放平台生态的所有教训都在为这两条背书。“先写使用代码”已成为测试驱动开发的日常。“难误用”原则被后续无数接口规范吸收(各类官方 API 设计指南里,“禁止返回 null”“用枚举不用魔法数字”都是标配条目)。JDK 的几个反面教材本身就是可验证的公开事实,无需辩护。
被时代修正的部分。 讲座成型的年代,受检异常(checked exception)还是 Java 正统,Bloch 当时的立场也是 orthodox 的;但生态很快用脚投票——C# 从一开始就没采用受检异常,Kotlin、Scala 干脆不支持,微软 C# 之父 Anders Hejlsberg 早在 2003 年接受 Artima 采访时就系统批评过它的扩展性问题,而 Bloch 本人在后来的交流中也把受检异常的使用场景收窄到了很窄的范围。连大师本人都会被时代修正,这恰恰说明接口设计没有 eternal truth,只有当下最优解。
“永不删除”也要打折扣。 “加得进、删不掉”是理想约束,但现实里 Java 自己就删过:JDK 9 的模块系统(2017)把大量内部 API 封装起来不再公开,JDK 11 移除了 Applet 支持,JDK 17(2021)将 SecurityManager 标记为废弃待移除。真实世界的规则更接近“删除是可能的,但代价极其昂贵且要排队很多年”——这不是对讲座的否定,而是给它补上了成本维度。
视野的时代局限。 这场讲座的语境是 2007 年的 Java 类库:进程内、编译期绑定、单一组织主导。今天的接口形态多了 Web API、GraphQL、gRPC、云服务的版本化协议,增加了幂等性、限流、向后兼容的网络层问题,这些讲座完全没涉及。但值得注意的是,谷歌后来发布的官方 API 设计指南,把这场讲座的思想直接扩展到了云端接口层面——核心判断标准没有变,变的是接口的载体。
第一条:把“先写调用代码”变成硬流程。 下一个接口开工前,先在注释或测试里写出三个调用示例——一个最简单的、一个最典型的、一个边界场景的。写不顺手就改签名,改到“像读句子一样自然”为止。评审同事的接口时,也优先看调用方的代码长什么样,而不是看实现多漂亮。
第二条:给每个公开成员做一次“误用审计”。 逐个问:这个方法最蠢的用法是什么?整数常量换枚举;布尔参数换成两个语义化方法;可能为空的返回值换成空集合或 Optional;会触发 I/O 的方法在命名里写明(fetch、load 与 get 区分开)。每修掉一个误用可能,就少写一段未来的兼容代码。
第三条:把可见性当预算管理。 新接口默认从最窄可见性起步,先在包内试用一个版本再考虑公开;公开时配上语义化版本号(SemVer),让“加功能”成为日常版本号跳动,让“改签名”成为需要公告的大事件。这样团队对接口的敬畏会从口号变成机制。
flowchart TD
A[API如钻石<br/>发布即永恒] --> B[好API四张面孔]
B --> B1[易学易用]
B --> B2[难以误用]
B --> B3[易于演化]
A --> C[设计期军规]
C --> C1[先写使用代码]
C --> C2[存疑就不加]
C --> C3[最小可见性]
A --> D[JDK血泪史佐证]
D --> D1[月份从零开始]
D --> D2[继承暴露一切]
D --> D3[equals藏网络请求]
A --> E[发布后的世界]
E --> E1[Hyrum定律生效]
E --> E2[删除昂贵且排队]
E1 --> F[语义版本化<br/>与兼容层兜底]
本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
