
Windmill codebase-design 技能设计深层模块的共享设计词汇与方法论【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill本文深入讲解 Windmill 仓库中.agents/skills/codebase-design/技能所定义的一套深层模块deep module设计方法论从 module / interface / seam / adapter 等核心术语的精确词表到删除测试、接口即测试面等设计原则再到依赖分类下的安全加深流程DEEPENING与并行子智能体接口探索Design It Twice。读完后你将掌握一套可直接用于模块重构、接口评审与可测试性设计的统一架构语言并理解 Windmill 的 AI 辅助开发体系如何把这套语言落地到实际的架构审查流程中。1. 文档在仓库中的位置Windmill 的 .agents 技能体系Windmill 是一个开源开发者平台内部工具、工作流、API 集成、后台任务与 UI 一体化其仓库在.agents/skills/目录下维护了一组面向 AI 编码智能体的技能skill文件例如adding-a-trigger、rust-backend、svelte-frontend、pr、grilling等。codebase-design是其中承担架构设计词汇层的一个技能其定义见 SKILL.md并配有两份延伸文档DEEPENING.md —— 如何在给定依赖条件下安全地加深一簇浅层模块DESIGN-IT-TWICE.md —— 用并行子智能体探索同一模块的多种截然不同的接口设计。需要说明的一点是根据 UPSTREAM.md 的记载codebase-design属于从外部公开技能仓库 vendored固定提交、MIT 许可进来的一组技能与grill-me、grilling、improve-codebase-architecture、domain-modeling共同构成一个依赖闭包——improve-codebase-architecture的架构词汇直接取自codebase-design因此移除或改写其中任何一个都会破坏其他技能。这也解释了为什么该文档反复强调术语必须原样使用use these terms exactly词表是多个技能之间的契约漂移即破坏集成。在 Windmill 中实际消费这套词汇的是 improve-codebase-architecture/SKILL.md。它明确要求智能体在扫描代码库寻找加深机会deepening opportunities时所有建议都必须精确使用codebase-design的术语module、interface、depth、seam、adapter、leverage、locality不得退化为 component、service、API、boundary。2. 核心设计目标深层模块技能开篇给出全篇的设计目标Designdeep modules: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface.即设计深层模块——把大量行为藏在一个小接口背后放在一条干净的接缝seam处并且能够透过该接口进行测试。文档给出的三个价值维度是对调用方的杠杆leverage for callers学会一小块接口换取大量现成行为对维护者的局部性locality for maintainers变更、缺陷、知识与验证集中在一处对所有人的可测试性testability for everyone调用方与测试跨过同一条接缝行为可通过接口直接观测。3. 共享设计词汇表Glossary这是整个技能最核心的部分。文档要求精确使用以下术语禁止用 component、service、API、boundary 等词替代——语言的一致性是这件事的全部意义Consistent language is the whole point。术语精确定义文档明确避免的替代词Module模块任何有接口、有实现的东西。刻意保持规模无关scale-agnostic可以是一个函数、一个类、一个包或横跨多个层的切片。unit、component、serviceInterface接口调用方为正确使用模块必须知道的一切类型签名只是其一还包括不变量invariants、顺序约束ordering constraints、错误模式error modes、必需配置、性能特征。API、signature太窄只指类型层面的表面Implementation实现模块内部的东西其代码主体。与Adapter区分一样东西可以是小适配器 大实现如一个 Postgres repository也可以是大适配器 小实现如一个 in-memory fake。当接缝本身是话题时用 adapter其他情况用 implementation。—Depth深度接口处的杠杆调用方或测试每学会一个单位接口所能驱动的行为量。接口小而行为多则模块深deep接口复杂到与实现差不多则模块浅shallow。—Seam接缝出自 Michael Feathers一个可以在不编辑该处的情况下改变行为的位置即模块接口所在的位置。接缝放在哪里本身是一个独立的设计决策与接缝后面放什么无关。boundary与 DDD 的 bounded context 语义冲突Adapter适配器在接缝处满足某个接口的具体实现。它描述的是角色占据哪个槽位而不是内容里面是什么。—Leverage杠杆调用方从深度中获得的收益每学会一个单位接口换更多能力。一份实现在 N 个调用点与 M 个测试中反复兑现。—Locality局部性维护者从深度中获得的收益变更、缺陷、知识与验证集中在一处而不是散落在各调用方。修一次处处修好Fix once, fixed everywhere。—值得注意的两个细节Interface 的定义远宽于方法签名。错误模式、顺序约束、性能特征都是接口的一部分——这意味着接口设计评审时不能只看类型。Adapter 与 Implementation 的分工解释了为什么文档禁止用 component/service同一对象在讨论接缝与讨论内部两种语境下应切换术语否则无法区分槽位与槽位里的东西。4. 深 vs 浅深度是接口属性文档用两幅 ASCII 图给出对照┌─────────────────────┐ │ Small Interface │ ← Few methods, simple params ├─────────────────────┤ │ │ │ Deep Implementation│ ← Complex logic hidden │ │ └─────────────────────┘┌─────────────────────────────────┐ │ Large Interface │ ← Many methods, complex params ├─────────────────────────────────┤ │ Thin Implementation │ ← Just passes through └─────────────────────────────────┘深层模块 小接口 大量实现浅层模块 大接口 少量实现应避免典型形态是接口几乎和实现一样复杂、实现只是透传。设计接口时文档给出三个自检问题能否减少方法数量Can I reduce the number of methods?能否简化参数Can I simplify the parameters?能否把更多复杂度藏到内部Can I hide more complexity inside?5. 四条设计原则SKILL.md 的 Principles 一节给出四条原则每条都是可操作的判定规则深度是接口的属性不是实现的属性。一个深层模块内部完全可以由小的、可 mock、可替换的部件组成——只是这些部件不属于接口。模块因此可以同时拥有内部接缝internal seam私有于实现、供模块自己的测试使用和位于其接口处的外部接缝external seam。删除测试The deletion test。想象删掉这个模块如果复杂度随之消失说明它只是透传pass-through如果复杂度会在 N 个调用方中重新出现说明它挣得了自己的存在earning its keep。接口就是测试面The interface is the test surface。调用方和测试跨过同一条接缝。如果你发现自己想测试接口之外past the interface的东西模块的形状多半是错的。一个适配器意味着假想接缝两个适配器才是真实接缝。除非有东西真的在某条接缝上发生变化否则不要引入接缝。第 4 条与 DEEPENING.md 中的接缝纪律Seam discipline相互印证单个适配器的接缝只是纯粹的间接层just indirection通常生产实现 测试实现这两个适配器同时成立时port 才值得引入。6. 面向可测试性的接口设计好的接口让测试变得自然而然Good interfaces make testing natural。文档给出三条规则并各配一组 TypeScript 对照示例规则 1接受依赖而不是自己创建依赖// Testable function processOrder(order, paymentGateway) {} // Hard to test function processOrder(order) { const gateway new StripeGateway(); }规则 2返回结果而不是制造副作用// Testable function calculateDiscount(cart): Discount {} // Hard to test function applyDiscount(cart): void { cart.total - discount; }规则 3保持小表面Small surface area方法越少 需要的测试越少参数越少 测试准备越简单。这条与第 4 节的深 vs 浅自检问题是一体两面小表面同时是调用方的杠杆来源和测试成本的下界。7. 概念间的关系Relationships文档用一个关系清单把术语网络钉死一个Module恰好拥有一个Interface它呈现给调用方与测试的表面Depth是Module的属性以其Interface为度量基准Seam是Module的Interface所在的位置Adapter位于Seam之上并满足InterfaceDepth为调用方产出Leverage为维护者产出Locality。这段文字实质上定义了一张概念图Module → Interface位于 Seam→ Adapter 满足之而Depth作为Module对Interface的度量派生出两个收益端。任何评审对话中若出现术语混用例如把 seam 说成 boundary都可以回到这张图仲裁。8. 明确拒绝的表述框架Rejected framings文档专门列出三个不采用的定义及其理由这对读者避免误用非常关键拒绝深度 实现行数 / 接口行数Ousterhout 的比值定义它奖励往实现里灌水。本文档采用depth-as-leverage深度即杠杆——调用方每单位接口知识换到的行为量。拒绝把 Interface 理解为 TypeScript 的interface关键字或类的公有方法太窄。此处的 interface 涵盖调用方必须知道的每一个事实不变量、错误模式、配置、性能特征。拒绝 Boundary 一词与 DDD 的 bounded context 语义重载。统一说seam或interface。9. DEEPENING给定依赖条件时安全加深一簇浅层模块DEEPENING.md 回答的问题是选定了一个加深候选deepening candidate之后它的依赖决定加深后的模块如何跨过接缝被测试。方法是先给依赖分类类别决定测试策略。9.1 依赖的四种类别类别定义可否加深与测试方式1. In-process进程内纯计算、内存状态、无 I/O。永远可加深——直接合并模块透过新接口测试无需适配器。2. Local-substitutable本地可替代依赖存在本地测试替身如 Postgres 用 PGLite、文件系统用内存实现。替身存在即可加深。替身在测试套件中运行接缝是内部的模块外部接口上不放 port。3. Remote but owned远程但自有Ports Adapters自己拥有的跨网络服务微服务、内部 API。在接缝处定义port接口。深模块拥有逻辑传输作为adapter注入测试用内存适配器生产用 HTTP/gRPC/队列适配器。4. True external真正外部Mock不自己控制的服务Stripe、Twilio 等。加深后的模块把外部依赖作为注入的 port 接收测试提供 mock 适配器。文档为第 3 类给出了一种标准建议句式可视为模板Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though its deployed across a network.在接缝处定义 port为生产实现 HTTP 适配器、为测试实现内存适配器使逻辑即使跨网络部署也仍位于一个深模块中。9.2 接缝纪律Seam discipline一个适配器 假想接缝两个适配器 真实接缝。除非至少两个适配器同时成立通常是生产 测试否则不要引入 port。内部接缝 vs 外部接缝。深模块可以拥有内部接缝私有于实现、供其自身测试使用以及位于接口处的外部接缝。不要因为测试用到了内部接缝就把它暴露到接口上。9.3 测试策略替换而不是叠加replace, dont layer浅层模块上的旧单元测试在深模块接口处的测试存在之后就变成了浪费——删掉它们新测试写在加深后模块的接口处。接口就是测试面测试断言通过接口可观测的结果而不是内部状态测试应当能在内部重构后存活——它们描述行为而非实现。如果一个测试因为实现变了就必须修改说明它测试越过了接口。10. DESIGN-IT-TWICE并行子智能体探索备选接口DESIGN-IT-TWICE.md 把 Ousterhout 的 Design It Twice 思想第一个想法不太可能是最好的操作化为一个三步流程面向 AI 智能体工作流第 1 步框定问题空间Frame the problem space。在派生子智能体之前先写一段面向用户的问题空间说明新接口必须满足的约束它将依赖的依赖项以及各自属于第 9.1 节的哪个类别一段粗略的示意性代码草图——不是提案只是让约束具体化。展示给用户后立即进入第 2 步让用户在子智能体并行工作期间阅读思考。第 2 步并行派生子智能体。至少 3 个每个必须产出截然不同的接口设计。给每个子智能体的技术简报相互独立文件路径、耦合细节、依赖类别、接缝后是什么且各带一条不同的设计约束Agent 1最小化接口——目标 1–3 个入口最大化每个入口的杠杆。Agent 2最大化灵活性——支持尽可能多用例与扩展。Agent 3为最常见的调用方优化——让默认场景平凡化。Agent 4如适用围绕 ports adapters 设计跨接缝依赖。简报中须同时包含 SKILL.md 的架构词汇与 CONTEXT.md 的领域词汇使各子智能体命名保持一致。每个子智能体必须输出五样东西接口类型、方法、参数外加不变量、顺序、错误模式展示调用方如何使用的示例实现藏在接缝之后的内容依赖策略与适配器对应 DEEPENING 的类别权衡——杠杆在哪高、哪里薄。第 3 步依次呈现并比较。按顺序呈现每个设计让用户消化然后用文字比较比较维度固定为三个depth接口处的杠杆、locality变更集中何处、seam placement接缝放置。比较之后必须给出自己的推荐——哪个设计最强、为什么如果不同设计的元素可以良好组合提出混合方案。文档特别强调要敢于下判断Be opinionated用户要的是一个强观点而不是一个菜单。11. 在 Windmill 仓库中如何被实际使用理解这套词汇的落地关键是看它的消费方 improve-codebase-architecture/SKILL.md 定义的工作流Explore探索先读项目的领域词汇表 CONTEXT.md再让子智能体走查代码库专门寻找理解一个概念需要在哪一堆小模块之间来回跳转哪些模块是浅的哪些纯函数只为可测试性而抽出、真正的 bug 却藏在调用方式里缺乏 locality哪些紧耦合模块在接缝处泄漏哪些部分难以透过现有接口测试并对任何疑似浅层的对象做删除测试——删除它会集中复杂度想要的信号还是只是移动复杂度Present呈现把候选加深机会写成自包含 HTML 报告每个候选一张卡片Files / Problem / Solution / Benefits / Before-After 图 / 推荐强度徽章Strong、Worth exploring、SpeculativeBenefits 必须用 locality 与 leverage 的语言表述。呈现时不预先提出接口而是问用户这些里你想探索哪个Grilling loop追问循环用户选定候选后走决策树约束、依赖、加深模块的形状、接缝后是什么、哪些测试幸存对话中若给加深模块起了 CONTEXT.md 里没有的概念名就补进词表若需要探索备选接口则回到本文的 Design It Twice 并行子智能体模式。这里体现出 Windmill 的双层词汇体系架构名词module、interface、seam、depth、adapter、leverage、locality固定来自codebase-design领域名词固定来自 CONTEXT.md其中钉住了 Windmill 的专有领域语言例如Step流中的一个节点代码中类型为FlowModule刻意避免与架构意义上的 module 混用——这正是 codebase-design 词表避免词清单在领域层的实际应用、Step setting重试、错误处理、超时、并发限制、缓存、防抖、提前停止、跳过、挂起、休眠、生命周期等按步运行时选项、Configured某个设置对象存在于步骤上刻意不等于会改变运行时行为sleep为0也算 configured、Trigger step轮询流的第一步空返回意味着无新数据流提前停止并标记 skipped 而非 failed、Member / Role / Owner等权限词汇以及各自的避免词。HTML-REPORT.md 进一步约束报告文风架构名词与动词必须直接取自/codebase-design词表简洁不是术语漂移的借口遇到不在词表里的词先找词表里现成的不要自造。从源码结构看这一整套约定共同保证了无论人类评审还是 AI 智能体输出谈论 Windmill 架构时 step intake module 这样的表达领域词 架构词是唯一合法形式而 FooBarHandler 或 Order service 一类命名会被视为词表违规。另外UPSTREAM.md 还记录了本仓库对上游技能做的最小化本地改动压平目录、把 markdown 链接改为仓库根路径的散文引用、移除 ADR 相关条款等并说明刷新方式——这对想理解为什么这些 SKILL.md 文件里用仓库根路径写同伴文件引用、而不是相对链接的读者是直接依据。12. 落地清单如何把这套方法论用到一次真实重构综合三个文档一次完整的发现浅层 → 设计深模块 → 落地流程可以归纳为识别对候选模块做删除测试检查它是浅层的接口复杂度 ≈ 实现复杂度、纯透传还是深模块分类按 In-process / Local-substitutable / Remote-but-owned / True-external 给依赖归类由此确定测试替身或 port/adapter 方案设计回答三个自检问题减方法、简参数、藏复杂度若拿不准走 Design It Twice用 3 个带不同约束的并行设计互相竞争比较固定按 depth、locality、seam placement 三轴比较给出明确推荐必要时混合落地与测试只在该模块的接口处写测试接口即测试面删除被取代的浅层模块旧单测replace, dont layer接缝上确认两个适配器成立否则先不引入 port保持语言一致全程使用本文词表领域命名同步更新到 CONTEXT.md。这套方法论的价值不在于任何单条原则而在于词表 原则 流程构成的闭环术语精确使评审可仲裁第 7 节的关系图原则给出判定规则删除测试、接缝纪律流程保证执行不漂移依赖分类决定测试策略Design It Twice 防止第一个想法赢。在 Windmill 这样一个横跨 Rust 后端、Svelte 前端、多语言脚本运行时的仓库里让智能体与人类共享同一套架构语言正是这套技能体系被引入.agents/skills/的直接目的。【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考