软件设计哲学
作者:Rob Zapp尚无安装尚无点赞更新于 2026年10月8日分类: 工程
它能做什么
在编写、修改或审查代码时使用,每当更改涉及添加导出或可导入的名称、创建模块、类、组件、辅助函数、钩子、服务或包装器、集中重复代码,或更改 API 时使用。遵循 Ousterhout 规则(深层模块、信息隐藏、降低复杂性),加上共享代码的不变量测试、阅读成本测试,以及最后必须附带设计说明。
安装会在你的 AgentsRoom 桌面端打开这个条目。如果还没有安装应用,你会被带到下载页面。
SKILL.md
--- name: 软件设计哲学 description: 在编写、修改或审查代码时使用,每当更改涉及添加导出或可导入的名称、创建模块、类、组件、辅助函数、钩子、服务或包装器、集中重复代码,或更改 API 时使用。遵循 Ousterhout 规则(深层模块、信息隐藏、降低复杂性),加上共享代码的不变量测试、阅读成本测试,以及最后必须附带设计说明。 --- # 软件设计哲学(John Ousterhout) ## 何时使用此技能 当你设计、编写、修改或审查代码时使用此技能。它适用于模块设计、API 变更、分解、重构、命名、注释、测试和性能工作。当某个变更感觉不自然,或一个变更涉及多个文件时,也应使用此技能。 ## 需要纠正的偏差 可运行的代码不等同于简单的代码。小的代码片段、熟悉的模式、标志、包装器和额外的文档可能会使设计更复杂。当它们增加了读者必须了解的内容,或当它们将知识泄露给其他模块时,就会产生这种情况。 ## 决策规则 - 通过设计减少复杂度的程度来衡量设计。优先选择能减轻读者负担的设计。复杂度有四个表现:一次变更需要在多个地方编辑;依赖关系被隐藏;步骤必须按固定顺序执行;读者必须记住许多事实。 - 将设计视为持续的工作。一个能工作的初始补丁如果使后续变更更难,则不算完成。对于接口、模块拆分或抽象的决策,比较两个或多个可能的设计。 - 优先选择深度模块。深度模块具有小接口并隐藏大量复杂性。拒绝传递服务、薄库包装器和小型辅助模块。拒绝任何增加名称但不减轻读者负担的提取。 - 设计接口应围绕调用者必须知道的内容,而非实现细节。避免脆弱的初始化序列、模式标志、配置旋钮和暴露内部选择的参数。 - 隐藏可能变化的决策。例如内部表示、存储结构、协议、文件格式和性能技巧。记账、归一化和边缘情况也是例子。将每个决策保留在拥有该知识的模块内。 - 将复杂性下沉到拥有细节的模块。接受更复杂的实现,只要它为调用者提供更简单的契约并消除每个调用点的重复工作。 - 在合适的层级使模块通用。不要为单一调用者定制模块。不要为未来需求添加模糊抽象。将罕见的边缘情况置于主路径之外,将特殊行为放在独立位置。 - 按总复杂度合并或拆分模块。不要按大小、代码执行顺序、习惯或外观合并或拆分。将相关状态、行为、规则和决策保持在一起。仅当新边界更深且读者能单独理解每一侧时才拆分。 - 减少异常集合。尽可能更改接口或规则,使无效状态无法发生。不要让每个调用者重复相同的防御代码。 - 使用注释减少复杂度。记录接口契约、必须保持的规则、隐藏的设计决策及其原因。还要写下调用者不应知道的困难事实。不要在注释中重复代码。不要用注释掩盖糟糕的命名、糟糕的拆分或令人困惑的控制流。 - 将命名、一致性和清晰度视为设计信息。名称告诉读者抽象,而非机制。相关操作使用相同约定。让读者感到意外的代码增加复杂度,即使代码很短。 - 针对公共契约和稳定 API 编写测试。通过这些契约测试隐藏的复杂性和特殊情况。不要让测试的便利性迫使接口变得浅显或泄露。 - 仅因两种原因之一添加性能改进、模式、范式或框架。它减少此代码库的复杂度,或有证据表明权衡是必要的。将每个优化隐藏在稳定接口后。 ## 信号及对应响应 - 功能不自然,或一次变更涉及多个文件,或审查者必须查找隐藏依赖。响应:查找缺失的信息隐藏和浅层模块。还要查找固定顺序的步骤,以及调用者承担的复杂度。 - 你添加了模块、层、服务、辅助、包装器或外观。或添加了模式、选项、回调或参数。响应:证明它隐藏的复杂度多于它增加的复杂度。 - 你更改了 API。响应:检查普通调用者必须知道的内容。调用者不应需要调用顺序、表示或存储。调用者不应需要传输、缓存、协议或文件格式。调用者不应需要内部工作流或许多初始化步骤。 - 你添加了特殊情况、标志、异常路径、条件或调用者可见的容器。响应:首先询问拥有模块能做什么。它可以移除无效状态、隔离异常行为或提供更强的操作。 - 你拆分代码、提取函数或添加变量。响应:检查新边界或名称是否有意义。它不应仅增加跳转、传递状态或调用者可见的中间步骤。 - 代码有阶段,如 `prepare`、`process` 和 `finalize`,或调用者必须分阶段构建对象。响应:检查时间顺序是否是真正的概念。如果不是,围绕稳定职责组织代码。 - 名称模糊、命名机制、不一致或让读者感到意外。响应:重新考虑抽象边界。不要接受几乎正确的名称。 - 注释冗长、重复代码、解释混乱接口或展示内部细节以说明用法。响应:更改抽象,或将缺失的契约移入接口。 - 你优化性能。响应:先测量,再隐藏优化。没有证据表明权衡必要时,不要放弃模块深度或信息隐藏。 - 你测试或审查。响应:关注公共行为和接口契约。还要关注稳定 API 背后的隐藏复杂度,以及抽象后面的特殊情况。 ## 最终检查清单 - 该变更是否减少了理解、修改、验证和扩展系统的工作量? - 每个接口元素、包装器、层、辅助工具、选项和名称是否隐藏了足够的复杂性以证明其合理性? - 重要决策是否集中在一个地方?依赖关系是否可见?调用者需要的约束是否被写下来?可变的内部实现是否被保护? - 常见情况是否无需额外步骤即可工作?罕见的控制、特殊情况、性能技巧和异常细节是否远离常见路径? - 名称是否准确且一致?注释是否是最新的,且不重复代码?代码是否遵循现有约定,除非有新信息需要更改? ## Gate 当变更添加了其他代码可以导出或导入的名称时,使用完整的检查清单。当变更创建模块、类、组件、辅助工具、hook、服务或包装器,或将重复代码集中时,也使用该清单。重命名、代码迁移、配置变更、数据变更和一行修复不需要使用。 ## 不变量测试:仅共享一起变更的代码 - 仅当共享代码保护一个你能命名的规则时才提取。证据是共变:历史显示这些副本是一起修复或更改的。仅看起来相似但独立变更的代码是押韵。保留押韵作为重复。三个相似代码块不证明规则。 - 修复必须消除问题,而不是转移问题。六个类型转换合并到一个通用转换辅助工具中仍是六个转换。写出类型映射器,替代隐藏的转换。 - 当抽象错误时,将代码放回内联,让重复返回。不要用标志曲解抽象。 - 不要仅因代码大小而拆分代码。一个400行隐藏一个决策的模块优于四个100行泄漏相同连接的模块。 - 机械地遵循Clean Code或SOLID(非常小的函数,每个职责一个类)会产生浅层模块。本技能优先于这种压力。 ## 读者成本:第三个测试 深度测试和不变量测试决定边界是否必须存在。读者成本测试决定边界周围代码是否易于修改。下一个读者,无论是人还是代理,都为必须阅读的每一行付费。代理以令牌计费。代理通过文本搜索、部分读取、类型检查和测试找到代码。 - **可查找。** 每个概念使用一个名称。全局拼写一致,方便纯文本搜索。缺陷:名称由字符串构建,通过导入副作用连接,单一概念有两个名称。隐藏定义的重导出链也是缺陷。 - **提前停止。** 在文件顶部或导出上方放置契约。说明承诺、隐藏内容和绝不做的事。这样读者可以提前停止。 - **机器可检查。** 每个边界输入输出使用精确类型,类型检查替代阅读调用者。缺陷:`any`、普通字典、含义仅在主体的布尔标志。 - **可见耦合。** 两处必须一起变更。用共享类型、测试或单一来源强制执行。不能时,在两处标记。 - **无噪声。** 删除重复代码的注释和被注释掉的代码。删除死分支和记录变更历史的注释。删除紧邻替代方案的旧路径。 - **可预测。** 遵循仓库现有布局。测试放在读者查找的位置,并能单独运行。 文件大小故意不在此列表中。非常大的文件是寻找第二个隐藏决策的理由,绝不是拆分文件的理由。 ## 安全性 对于现有代码,先写测试保持当前行为,然后使模块更深。对于新代码,写定义预期行为的测试。 ## 设计说明(当Gate适用时必填) 当Gate适用时,在拉取请求描述中添加标题为`## Design note`的章节。写两到四行: - 你添加的每个边界及其隐藏的决策。 - 你故意保留的每个重复及其原因。 - 你接受的每个浅层部分及其原因。 如果Gate不适用,写`## Design note`后跟`Gate not applicable: <reason>`。也将设计说明放入最终步骤的摘要中。 ## 审查模式 当你审查或测试其他代理或人员编写的代码时使用本节。 1. 检查设计说明。当Gate适用且拉取请求无`## Design note`章节时,报告阻塞性发现。当说明与差异不符时,报告阻塞性发现。 2. 设计发现仅在满足以下两个条件时为阻塞: - 它命名了本技能的规则。该规则是决策规则、Gate、不变量测试或读者成本项之一。 - 它陈述了对读者或下一次变更的具体成本。例如:“调用者必须知道存储形态。”“一个概念有两个名称。”“帽子变更需要编辑三个文件。” 3. 将其他设计观察标记为非阻塞。放入标题为“非阻塞设计说明”的单独列表。非阻塞说明永远不会将工作退回给构建者。 4. 不要将偏好报告为发现。不同名称、文件布局或风格是偏好。仅当违反命名规则且有具体成本时,才成为发现。 5. 当相同设计发现第二次审查时出现,升级处理。不要第三次请求相同更改。 ## 相关技能(安装时) - `find-shared-code`:仅报告最近历史中值得共享的代码搜索。使用本技能的不变量测试和深度测试。 - `refactoring`和`working-effectively-with-legacy-code`:向更深设计迈进的安全步骤。本技能决定新边界是否保留。 ## 来源和许可 该技能基于 GitHub 上 ciembor/agent-rules-books 仓库中的《软件设计哲学》“迷你”规则(MIT 许可证,提交号 893a88a)。门控、不变量测试、读者成本测试、设计注释和审查模式是对这些规则的补充。该仓库还包含了该书的完整规则。
标签
设计架构ousterhout审查
延伸阅读
Claude Ads:帮你审计广告账户的 Claude Code 技能
Claude Ads 是一款面向 Claude Code 的开源技能:对 Google、Meta、LinkedIn、TikTok、Amazon 广告等做 250 多项检查,给出百分制评分和按优先级排序的行动方案,全程只需十来分钟。本文讲解安装、命令、局限,以及如何在 AgentsRoom 中把它编排起来。
AGENTS.md:一个上下文文件喂饱所有编码 Agent(Codex、Antigravity、Claude)
AGENTS.md 是 AI 编码 Agent 在动你代码之前先读的那份可移植指令文件。该往里写什么、它和 CLAUDE.md 有何区别,以及如何在 Codex、Antigravity 和 Claude 之间保持同一份上下文。
下载 AgentsRoom
在一个窗口中运行你所有项目的所有 AI 代理。
免费下载 AgentsRoom
配套应用:随时随地监控你的 Agent
使用 Claude、Codex、Antigravity CLI 或其他 AI 提供商。
获取扩展程序
Chrome Web Store
把 Bug 和需求直接发送到您的公开待办清单。