Community编程与开发github.com

ShortArrow/library-design

设计库——无论是发布的包还是应用内模块(当有第二个消费者时)。实现可以永远保持可编辑,而公共 API、依赖集、性能特征及所有可观察行为在首次外部使用时即冻结(海勒姆定律)。对调用者的五项职责:不夺取控制权、不增加隐藏成本(零成本抽象为基准)、不持有隐藏状态、不隐藏失败、不阻塞逃生路径。适用于设计或审查公共 API、提取共享代码为模块、被要求“使其可重用”、签名兼容的更改改变可观察行为、或应用围绕框架的类型和生命周期构建时。

library-design 是什么?

该技能明确规定了库设计的严格契约:公共 API 一旦暴露,所有可观察行为(包括异常措辞、枚举顺序、日志输出等)都根据海勒姆定律冻结,后续更改必须遵循语义化版本规范。它提出了五项核心原则:不夺取控制权(机制与策略分离)、不增加隐藏成本(零成本抽象、有界缓冲、依赖作为 API 的一部分)、不持有隐藏状态(实例化而非单例)、不隐藏失败(类型化错误处理、保留错误原因、设计取消机制)、不阻塞逃生路径(双层 API、可注入的观测工具)。 该技能适用于任何编程语言和平台,尤其适合设计或审查公共 API、提取共享代码为模块、或处理语义化版本变更决策。核心工具是严格区分冻结/可编辑层,使用语义化版本和五项职责作为评审标准。当模块只有一个消费者时(语义化版本 0.y.z),可暂缓实施,但在第二个消费者出现时必须立即采用完整契约。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ShortArrow/skills/tree/HEAD/skills/library-design

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

库契约

应用程序由用户评判; 库由程序员评判, 并且是在特定的时刻——当他们想要作者未曾料到的东西时。 一个好的库不是按预期使用时很方便的那个, 而是在其他用途下也不必被抛弃的那个。

以下所有规则都源于一个不对称性。 在库内部,你可以永远改变任何东西。 而在外部,一旦有人依赖,调用者可观察的一切都会冻结—— 并且当调用者足够多时, 所有的一切都会被依赖。

什么冻结,以及何时冻结

海勒姆定律是运作条件,而非奇闻: 当用户足够多时, 每一个可观察行为都会成为承重行为——异常措辞、 枚举顺序、日志输出、计时, 甚至是你文档化为未定义的行为。 “合同中从未包含”并不能挽回已经基于此发货的调用者。

因此,冻结线位于可观察性,而非 public 关键字, 并由此产生三个后果:

  • 表面从一开始就很小。 一个符号,一旦公开, 就是一个你可以拓宽但永远不能悄无声息地收回的承诺。 “暂时公开”是为了毫无好处而欠下的债务;按需暴露。
  • 格式比代码更持久。 文件格式、网络协议、 持久化模式在围绕它的代码每次重写后都存活。 将内部结构直接序列化会将冻结层与可编辑层焊接在一起。
  • 应用内模块在其第二个消费者处越过这条线, 而非在打包时。 一旦存在两个调用者, 其 API 的冻结经济学就与已发布的包相同—— 只是爆炸半径较小。

语义化版本是同一冻结线的公开拼写, 并且它从拒绝没有版本号开始: “使用语义化版本控制的软件必须声明公共 API”(SemVer 2.0.0,第 1 条)。 一旦声明,增量是决定的而非感受的—— 对该 API 的向后不兼容更改必须提高 MAJOR(第 8 条), 新的向后兼容功能必须提高 MINOR(第 7 条), 向后兼容的修复必须提高 PATCH(第 6 条)。 海勒姆定律正是使声明变得困难的原因: 规范冻结了你声明的, 而调用者冻结了他们观察到的。

该规范也为尚未越过线的模块命名: “主版本零(0.y.z)用于初始开发。任何内容都可能随时更改”(第 4 条)。 这是只有一个消费者模块的诚实标签, 当它获得第二个消费者的那天,就是 1.0.0 应被发布的日子, 而非打包的那天。

不夺取控制权

将机制与策略分离。 执行 HTTP 请求是机制; 使用指数退避重试三次是策略, 而策略被内化到核心中,会缩小库的范围到只有共享它的调用者。 为日志、线程、重试、超时、 缓存、序列化提供方便的默认值—— 并使每一个都可替换。

应用程序的角色是单一的,并且不属于你。 退出进程、写入标准输出、捕获 SIGINT、 安装顶层异常处理程序、调整线程池大小: 每个进程只能有一个组件执行这些操作, 而执行这些操作的库会与有权执行这些操作的那个组件发生冲突。 读取进程的输入也属于同一角色: 环境变量、 配置文件和命令行参数由应用程序根读取, 然后将每个值作为参数或选项对象传递给库。 调用者选择值的来源, 测试则可以直接传入值,而无需搭建运行环境。

魔术——自动检测、自动转换、自动重连、 自动缓存——仅在调用者能够理解其行为原因时才可接受。 用户无法调试的便利是夺取的控制权,而非节省的工作。

不增加隐藏成本

事情发生的时间属于调用者。 启动线程的导入、打开连接的构造函数、 读取环境变量的静态初始化器——都将实际工作移动到了调用者从未选择且无法回退的时刻。

依赖项是你 API 的一部分。 它的运行时间、更新节奏、ABI 稳定性、许可证、 以及它自身的初始化副作用都会传递给每个调用者; 你是在替他们做选择。

性能特征是契约。 将 O(1) 查找变为 O(n)——或者加倍分配和拷贝—— 是穿着兼容签名的破坏性变更, 而调用者会在生产中发现它。

当输入到达速度快于离开时——流、事件、日志、 队列——无界内部缓冲区是延迟的内存泄漏。 反压是设计的一部分,而非操作细节。

抽象本身的基准是零成本, 同时满足 Stroustrup 的两个条款。 调用者未使用的,他们不付费: 便利层不得以其分配、 其分发或其设置来征税原始层。 而且他们使用的,他们无法用手写代码合理写出更好的: 被手写代码超越的抽象会被绕过, 而被绕过的库是戴着版本号的废弃品。

不持有隐藏状态

单例、静态缓存、全局事件循环、 进程级配置: 当只有你的库在进程中时,它们都能工作, 但当第二个库、插件宿主、 或并行测试套件共享它时就会发生冲突。 状态存在于调用者创建、拥有、 并且可以创建两次的实例中。

不隐藏失败

API 形状声明了可能发生什么以及谁负责。 错误、取消、超时、部分成功、 不存在——通过类型建模, 而不是通过“这通常不会发生”和异常来绕行。 相同的形状承载了注释无法强制执行的所有权答案: 谁关闭流,缓冲区是否可重用, 回调到达的线程。

保留原因。 将失败折叠为 false 或 null 恰好剥去了上层所需的决策信息; 自己记录并继续丢失了相同的信息,并劫持了调用者的日志策略。

质量在异常路径上区分。 在快乐路径上,库是可以互换的;超时、重连、 流中断开、部分读取、 损坏数据和重试是差异所在, 因此首先为它们的组合设计。

取消机制难以事后添加——取消令牌会穿过它所触及的每个签名。 I/O 和长时间运行的操作从第一个版本起就接受它。

不阻塞逃生路径

构建两层。 一个高级 API,用一行代码服务常见情况, 建立在公共原语之上——ReadAsync() 基于 ReadAsync(Memory<byte>)—— 这样,当调用者的需求超出顶层时,他们可以向下爬而不是离开。 只有高层的库在遇到未预期的第一个需求时就会被抛弃。

可观测性是被注入的,而非拥有的:日志器、指标、 追踪插入调用者的堆栈。 只有你的库能读取的诊断格式是劫持的遥测记录。

一项税务,两种货币

此技能与整洁架构是在两种货币中的同一拒绝。 零成本抽象拒绝运行时性能税; 依赖规则拒绝可变更性税。 在两者中,抽象均由其迫使依赖方放弃的东西来评判。

这里的职责其架构名称
机制与策略分离用例与框架和驱动程序分离
默认值可替换依赖倒置——稳定端选择实现
便利层之下的原语外层细节可替换
无全局状态依赖项显式,因此可划定边界
依赖项是你 API 的一部分依赖规则

调用者的健康形态将你的库置于底部: 是可替换的细节,位于他们的策略和领域之下。 相反的形态——应用程序生活在框架内部, 拖着它的生命周期、它的类型、它的依赖注入、它的错误模型、 它的异步模型——是库扮演应用程序, 以架构规模书写。

一般形式:好的架构最小化的不是依赖项的数量, 而是每个依赖项所夺走的自由。

总结

库中的抽象存在是为了拓宽调用者的选择。 一旦它以性能、控制或信息作为交换, 它就停止了抽象并开始做决定。

何时不适用

只有一个消费者、一个仓库、 一个团队的模块尚未跨越冻结线——它在语义化版本术语中是 0.y.z, 无论它是否携带数字—— 为它构建每个可替换的策略接缝是带着维护账单的投机。 保留从第一天起就零成本的纪律——无全局状态、 保持原因的失败、 不在调用者要求之前进行任何工作—— 并在第二个消费者到达时添加接缝, 此时它们就不再是投机了。

来源

  • 语义化版本 2.0.0,semver.org: 第 1 条(公共 API 必须声明), 第 6-8 条(PATCH、MINOR、MAJOR 增量),第 4 条(0.y.z), 以及关于弃用的 FAQ 条目(记录,然后发布包含弃用的次要版本)。 措辞已对照页面检查。
  • 海勒姆定律是一个观察结果,而非标准,此处作为一条观察引用。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

ShortArrow/interface-design

Deciding how a screen looks and behaves, triggered by the moments that hand the decision to the user unworked: about to ask which layout, which colour, which placement, how to draw a diagram, how wide a pane, how to split a view; about to call a visual choice a matter of taste; about to treat "it looks fine" as having checked it; about to ship a control sized by eye; about to read an automated accessibility pass, or a Lighthouse score of 100, as a clean bill of health. Most of these have an answer in a published standard, a measurable threshold, or one screenshot — WCAG 2.2 fixes contrast, target size, reflow and focus; a design system fixes spacing and components; axe-core finds about 57% of WCAG issues, and Lighthouse's accessibility score is that same engine weighted, so both leave the rest to a person. What survives is a genuine question, asked with the evidence attached. Use before asking any question about appearance, layout or interaction, and before calling

ShortArrow/test-design

Deriving test cases, triggered by the moments that produce weak ones: about to write a test list that is the happy path restated, about to derive tests by reading the implementation they check, about to test the bound the specification names and not the other one, about to enumerate the roles the specification lists and skip the caller it forgot, about to vary one condition at a time from the happy path, about to draw a state or decision table without tying each row to a test, about to write an expected result with "or" in it, about to skip the error paths as unlikely, about to leave a path unimplemented and untested, or about to grade a suite as adequate by reading it. A test derived from the implementation passes by construction; coverage counts classes, not tests. Use when writing a test plan or list, when a use case is the specification in hand, when adding tests to existing code, when reviewing a suite for gaps, and when a suite is all green and proves little.

相关技能