domain-modeling 精读:术语是契约,值得在会话中途打断你
domain-modeling 把术语当契约维护:冲突当场裁决、就地写进 GLOSSARY.md、用边界场景压力测试概念、代码和说法打架时把代码摔回来。本文讲清它为什么区分『主动改模型』和『被动读词表』,它给 glossary 和 ADR 设的两条不同门槛,以及几个它没藏起来的代价——自动触发最弱、未评审的 agent 写 glossary 比没有更糟、它并不能凭空生成领域语言。
(本文读的是 mattpocock/skills 快照 5beb35b,2026-10-19。引用原文都能按文中路径回仓库核对。这是「逐个技能精读」系列的第十四篇,聚焦 domain-modeling 一个技能。)
它改的是词,不是代码
skills/engineering/domain-modeling/SKILL.md 开头把两种用法分得很干脆:
Actively build and sharpen the project’s domain model as you design. This is the active discipline: challenging terms, inventing edge-case scenarios, and writing the glossary and decisions down the moment they crystallise. (Merely reading
GLOSSARY.mdfor vocabulary is not this skill: that’s a one-line habit any skill can do. This skill is for when you’re changing the model, not just consuming it.)
读词表是任何技能都能做的一行习惯;这个技能只在改模型时才该上场。触发方式是 /domain-modeling,或者被 grill-with-docs、wayfinder、triage、improve-codebase-architecture 在底下加载。
产物是两个落到仓库里的文件:
GLOSSARY.md(多上下文时,是根目录GLOSSARY-MAP.md指向的一组GLOSSARY.md)docs/adr/NNNN-slug.md
两个都是惰性创建:Create files lazily: only when you have something to write.
它怎么工作:五个动作,全发生在对话里
SKILL.md 的 During the session 一节列了五个动作,前四个是提问,第五个是落盘:
- Challenge against the glossary. 用户用的词和
GLOSSARY.md里的定义冲突时,当场点出来:"Your glossary defines 'cancellation' as X, but you seem to mean Y. Which is it?" - Sharpen fuzzy language. 含糊或超载的词,提一个精确的规范词:
"You're saying 'account': do you mean the Customer or the User? Those are different things." - Discuss concrete scenarios. 讨论关系时,编边界场景逼用户把概念之间的界线说清楚。
- Cross-reference with code. 用户说的和代码矛盾时,把代码摔回去:
"Your code cancels entire Orders, but you just said partial cancellation is possible. Which is right?" - Update GLOSSARY.md inline. 术语一旦定下来,就在那里写进 glossary——
Don't batch these up: capture them as they happen.
文档把这第五步的意思压得很重:A glossary written at the end is a summary of a session; the inline version is the session’s output. 结尾一次性整理的词表是「会话的摘要」,边聊边写的那份才是「会话的产出」。
两个产物,两条门槛
这是这个技能最值钱的一处设计:它把 glossary 和 ADR 的门槛分得很开。
GLOSSARY.md |
docs/adr/NNNN-slug.md |
|
|---|---|---|
| 装什么 | 术语。一两句话讲清它是什么,被放弃的同义词挂在 _Avoid_ |
一个决定,一到三句话:背景、选择、理由 |
| 门槛 | 一个含糊的词变成规范词 | 三条全中 |
| 何时写 | 就地,术语定下来的那一刻 | 主动提议,不默认写 |
| 永不装 | 实现细节、spec、草稿本、通用编程概念 | 一次会话里每个选择的日记 |
ADR 的三条门槛,SKILL.md 用的是三个问句:
- Hard to reverse: the cost of changing your mind later is meaningful
- Surprising without context: a future reader will wonder “why did they do it this way?”
- The result of a real trade-off: there were genuine alternatives and you picked one for specific reasons
If any of the three is missing, skip the ADR.
三条缺一就跳过。文档给每种「缺」都配了一句判词:好反悔的,你回头就反悔了;不意外的,没人会问为什么;没有真选项的,除了「我们做了显然的事」没什么可记。而 glossary 有一句比格式更硬的禁令:
GLOSSARY.mdshould be totally devoid of implementation details. Do not treatGLOSSARY.mdas a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else.
它和「一般技能」的差别:它是一次打断
一般技能给你一段流程,你走完它。domain-modeling 的动作是在你说话的中途停下来,指出你刚用的词和契约不符。文档把这层价值说得很准:glossary 的收益在上游——命名和概念对齐,不在聚合和分层仪式;Synonym control matters at naming boundaries: module names, table names, status enums, issue titles, CLI commands. It matters much less in ordinary prose.
另一处让它区别于「一次性产出」的设计:GLOSSARY.md 被要求既能变长也能变短。验收判据里有一条 GLOSSARY.md gets shorter as often as it gets longer——术语被合并、被否掉,也是一种结算。
它被谁加载:一套技能底下的一层
它很少单独跑。文档列的用法全是被别的技能在底下调用:grill-with-docs 在一次盘问里驱动它、wayfinder 在画地图时加载它、triage 用它让工单保持项目自己的用词、improve-codebase-architecture 在决定落定时调用它。它的近邻是上一章的 codebase-design——一个管问题域的用词,一个管模块的形状,两者通常一起要,因为给一个深模块起名需要同时用到两套词汇。
多上下文的仓库里,它靠根目录一份 GLOSSARY-MAP.md 找到每个上下文各自的 GLOSSARY.md;没有 map 时按单上下文处理,两份都不存在时,第一个术语定下来才建。文档同时提醒了一句关于 DDD 的实话:DDD gets less useful the closer it gets to the implementation,这套东西的收益在冲突最密的地方(命名边界),在普通行文里收益很小——所以一天的小活可以跳过它。
它有意识摒弃了什么
一、摒弃了「读一遍 glossary」的被动用法。 读词是任何技能都能做的习惯;这个技能只在改模型时上场。
二、摒弃了把 glossary 当 spec 或草稿本。 见上一条禁令。
三、摒弃了通用编程概念进术语表。 格式文件给了一条自检:is this a concept unique to this context, or a general programming concept? Only the former belongs.(超时、错误类型、工具函数都不进。)
四、摒弃了预先建文件。 第一个术语定下来才建 GLOSSARY.md,第一条过门槛的 ADR 才建 docs/adr/。
五、摒弃了 ADR 的模板仪式。 ADR 可以只是一段话——The value is in recording *that* a decision was made and why, not in filling out sections. 三个可选节(Status / Considered Options / Consequences)只在真加价值时才加。
六、摒弃了「一次性穷举」。 文档给了棕地仓库的正解:别等词表慢慢攒,直接点名让 grill-with-docs 审一遍——Expect a long interrogation. One user reported more than 50 questions before the file was in shape.
原文证据
先看它的产物长什么样。GLOSSARY-FORMAT.md 的样张里,一个词条只有两行:
**Order**:
{A one or two sentence description of the term}
_Avoid_: Purchase, transaction
两条规则值得抄:定义只说它是什么,不说它做什么;同一概念有多个词时,选最好的那个,其余全挂 _Avoid_。
再看它的验收判据:
- It stops you mid-sentence to ask which of two things you meant, instead of picking one and moving on.
GLOSSARY.mdchanges during the conversation, not in a burst at the end.- It refuses to write an ADR for something you could undo tomorrow, and says which of the three tests failed.
几个它没藏起来的代价
一、自动触发最弱。 文档直说 In practice, automatic invocation is the weakest part of the skill. 被 grill-with-docs 点名加载时,模型常常只加载 grilling 而跳过它,判据是会话结束后 GLOSSARY.md 纹丝不动。所以文档的建议是按名字显式调用。
二、它会产出「自信的错话」。 一条很硬的警告:an unreviewed, agent-authored glossary is worse than none: it fills with confident claims that later sessions treat as true. 不只是没用,是有负作用——后续会话会把那些断言当成事实。
三、它不能凭空生成领域语言。 文档把「把含糊 prompt 变成领域语言」这个问题关掉了:A domain language you do not understand yourself is meaningless once written down. 相关陷阱是把词当成建模——The right nouns on top of the wrong concepts produce output that reads as correct and is not.
四、文件名本身没定论。 GLOSSARY.md 是整套技能里被争得最多的一处,且没有结论;至少有人为了改名维护一个本地 fork,但改名的代价是其它所有技能都在找 GLOSSARY.md。
可迁移结论
- 把术语当契约维护,而不是当文档。 冲突当场裁决,裁决当场落盘。批量的词表是会话的摘要,不是会话的产出。
- 给两个产物两条门槛。 术语用「含糊变规范」,决定用「难反悔 + 不意外 + 真权衡」三条全中。门槛分开,两者才不会互相污染。
- 写成「是什么」,并列出你放弃的词。
_Avoid_那一行是防漂移的关键。 - 让一个文档允许变短。 「变长变短一样频繁」是一条可执行的健康判据。
- 承认负作用。 未评审的 agent 写 glossary 比没有更糟——所以要么评审,要么别写。
domain-modeling 是这个系列里最「不像技能」的一篇:它没有步骤、不给产物模板、也不试图让 agent 更聪明。它只做一件事——在你用错词的时候,让你停下来。而它最诚实的地方,是把它做不到的事(自动触发不可靠、不能替你生成语言、写不好反而有害)一并写进了同一份文件里。
讨论
用 GitHub 账号留言;评论保存在公开仓库chengshu-blog-discussions的 Discussions 里。也可通过 RSS 订阅后续文章。