mcpField
T
tlc-discover

tlc-discover

github.com/tech-leads-club开发7.1k93 次安装2 天前更新
代码仓库

关于 tlc-discover

tlc-discover 是一项 Agent 技能,帮助先了解项目现状、厘清问题并作出判断,再考虑解决方案。它通过访谈引导决策,并生成按垂直切片组织的设计文档。

主要功能

  1. 01从现状到决策的访谈流程
  2. 02区分痛点、能力缺失与建设任务
  3. 03要求决策有事实依据
  4. 04按垂直切片组织设计文档
  5. 05将未决问题转交 RFC 或 spike

适用场景

  1. 01选择方案前评估待开发功能
  2. 02厘清用户影响与现有替代方案
  3. 03记录供团队规划使用的决策

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tech-leads-club/agent-skills --skill tlc-discover

安装后在 Claude Code 中输入/tlc-discover确认已加载。

附带文件references/document-format.md

详细介绍

TLC Discover

找出这个项目的真实现状。理解问题。决定是否解决它。然后,也只有到那时,才决定如何解决。

SITUATION ────→ PROBLEM ───────→ VERDICT ───────→ DECIDE
(where this     (no solution     (a stop, or a    (two shapes, costed
 project is)     proposed yet)    record)          against this repo)

这些不是一套脚本,而是前置条件的顺序。 你要进行的是一次访谈:根据已经确定的事项,提出所有能回答的问题;没有可回答的问题时就停下来。顺序会自然显现,因为 如何 以 是否 为前提,而 是否 又以 我们现在处于什么位置 为前提。把它们按四幕推进,就是在探索阶段询问一个从未交付的项目:它的问题如今要付出什么代价。

这里真正需要避免的失败,不是凭空捏造事实,而是过早收敛:第二轮就提出解决方案,听到一句“可以”,便炮制出一个看似经过审议、实则毫无审议的决定。以下一切都是为了让这种情况更难发生。

最终产物是一份设计文档,任何人——无论是个人、团队还是规划工具——都能据此制定计划,而不必参加产出这份文档的对话。它必须展示设计,而不只是记录设计已完成:难以撤销的决定、关键路径的流程、各个状态及其变更者、实际采用的架构,以及调用方将依赖的契约。关系或顺序是内容本身时,就用图表示;列出三十个字面值的表格,只是披着设计外衣的规范。文档还应按垂直切片组织,这样,想了解某一部分工作的读者只需阅读一个章节,就能找到其中的差异、流程、架构、契约、决定和待解问题。如果 Shape 无法告诉读者这个方案押注什么,某个切片无法展示其部分如何运转,或 Work 索引无法指出哪些切片已明确,那么即使记录了每项决定,这份文档仍然不合格。

关键规则

  1. 结论出来之前,不得提出任何技术方案。 无论是库、服务提供商还是模式,都不行。如果问题部分已经在为某个方案辩护,说明问题框定本身已经变成了解决方案。这禁止的是提出方案,而不是了解现状:项目已经在运行、已经承诺采用或已经写了一半的内容,都是约束;如果到后期才发现这些约束,就会重新打开团队上个月已经敲定的事项。
  2. 只要决定尚未确定,结论就必须成为暂停点。 提出结论,然后等待。如果 Situation 已经确认有人作出了承诺,那么这只需记入记录,而不是设为关卡——明知答案却还要设关卡,只会沦为审批作秀,让所有人养成点击通过的习惯,最终也会错过真正重要的关卡。
  3. 绝不提出你不会交付的选项。 每次都要考虑两种方案;只有第二种方案确实可行时,才值得单独成节。如果不可行,就用一句话说明它要胜出必须满足什么条件——这也正是规划者需要知道的、足以排除它的属性。
  4. 先说清哪个数字会改变决定,再去获取它。 没有对应问题的数据只是噪声,还会占用上下文。并且,缺少数字并不能说明问题本身是什么样——通常只能说明仪表化不足。应当询问,绝不要从沉默中推断规模。
  5. 没有具体值的决定就不算决定。 形态、界限、状态、字段,都必须明确。下游环节无法处理模糊输入;现在把它弄清楚,成本最低。
  6. 影响高而清晰度低的问题,不应在此处决定。 应将其转为 RFC 或 spike。强行作出决定,会产出代价最高的文档:看似已定,实际未定。
  7. 针对当前情境解决问题,而不是照搬参考架构。 推荐方案应是足以解决经测量确认的问题的最小方案;任何更重的方案,都必须有当前成立或有可信可能近期成立的条件作为依据。仅用于拓宽读者视野的选项也可以列出,但要明确标注其用途——每项只写一句,绝不伪装成候选方案。

访谈

询问当前能够回答的问题。所有前置条件已确定的问题都可以问;前置条件尚未确定的问题则不行,因为对它的回答只是猜测,而你之后会把猜测当作发现。当没有剩下可回答的问题时,访谈就结束,而不是等到下面的各个章节都问过一遍;即便某个重要问题属于尚未涉及的章节,也仍然必须提出。

**这里没有任何内容仅仅因为存在,就必须写成一段。**没有输入的步骤只占一行,没有内容的章节就完全不出现——当然也不该把它写成一个标题,下面加上“N/A”,仿佛道歉就能让形式感变得不那么形式。这条原则对问题和文档同样适用,也正因如此,同一项技能才能服务于两小时的改动,也能服务于一季度的工作。

**每个问题都要在同一行附上你的建议答案和理由。**这样,用户同意只需一个词,不同意则只需一句话;而不附建议的问题,会把用户来这里想让你完成的工作又推回给用户。通常,想不出建议答案的问题,应该先去查。

**事实靠查,决策靠问。**凡是能从代码仓库、跟踪系统或文档中确定的事情,就去查清楚——把本可以读到的事实拿来占用别人的注意力,只会让这次会话落得个像填表的名声。产品观点、优先级和风险承受度,只能由他们自己决定。

尽管待解决的问题范围很广,也要控制每次交付的规模:答案彼此依赖时问一个,互不依赖时问两个。问题范围决定可以问什么,以及何时结束,但绝不决定一次问多少个。一次问十个就是把表格一股脑丢给对方,而人们面对一股脑丢来的表格,通常只会全部同意。

**有些问题光靠交谈根本无法回答。**比如应该是什么感觉、是一页还是三页——这些问题需要有具体内容供人评判,反复讨论只会让会话变长一倍,却毫无进展。发现这种情况时,当场调整方向:交给一次探索性开发,因为只有动手构建才能得出答案;或者交给设计师,因为只有亲眼看到才能判断。调整方向本身就是成果,并不意味着信息提取失败。

情况

以下三个事实决定了下面哪些问题值得提出;如果判断错了,调研就会让人觉得你在采访别人的项目。

项目目前处于什么阶段。 稳定投入使用的产品有一个可以衡量的当下。尚未发布的项目根本没有当下——不是当下很小,而是完全没有——因此关于当下成本的问题会连续四次得不到答案,最后看起来就像依据不足。处于积极开发中的项目,手头往往还有比指标更有用的东西:有人写下的路线图,以及有人正在编写、尚未完成的代码。

决策是否仍未敲定。 有时还没人做出决定,而这正是开展调研的全部原因。有时路线图、季度计划或某位资深人士已经作出承诺;这时诚实的做法是记录是谁作出的决定,并把这次会议用于讨论具体形态。先询问,不要自行假设:这两种情况几乎没有相同的问题。

与此相关、目前正在推进的工作。 探查容易获取且现成的信息——活跃分支、问题跟踪器中当前开放的周期、已连接的知识库、此技能已经产出的设计文档——并且询问其他情况,因为在场的人知道团队上周开始了什么,而这比任何探查都可靠。不要假设这些东西一定存在:没有跟踪器、没有 Wiki 的项目很常见,而代码仓库加上回答问题的人,始终足够。

利害有多大。 关键不是工作要花多久,而是判断错了要付出多大代价。一个人用一个下午就能撤销的改动,与迁移所有人数据的改动,在路线图上可能规模相同,但在这里完全不是一回事。这个事实决定了其余环节要进行到什么程度。

正在推进的工作会改变答案,而不只是充当背景。其他地方正在开发一半的功能,会让这次工作变成对它的扩展。早先设计文档中已经敲定的决定,属于已确定的输入;重新讨论只会徒增折腾,披着全面审查的外衣。有人正在积极重写的文件,则意味着一场本可以避免的冲突——趁现在选择合适的衔接点,还不费什么力气。

每项各用一行记录下来,这样六周后,读者就能看出问题部分之所以单薄,是因为项目尚未发布,而不是因为没人想到这些问题。对正在推进的工作,用一句话说明哪些内容会沿用为先例,再用一句话说明哪些内容不在范围内——提交哈希和方法名最容易从这里漏进文档,而这两者都不该出现。

有些工作根本不需要调研;说明这一点也是职责的一部分。 如果利害很小、答案可以在一个下午内撤销,而且在场的人没有异议,就用一段话给出建议,然后结束:不写文档、不下结论、不设章节。不能否决功能的调研,只是走过场;不能否决自身的调研,则是人们会学着绕开的文书流程——最终,它就不会再用于真正需要它的决策。标准是三项同时满足:容易撤销、影响范围小、无人有异议。缺少任何一项,都要进行调研。

问题

这一阶段不会发生任何技术工作。人们带着一个解决方案到来——“我们需要缓存”“我们应该接入 Stripe”——首要任务是找出这个方案原本要解决的问题,因为他们带来的方案通常只是最先想到的,而不是经过比较后选出的。

先判断这属于哪类问题,因为不同问题要问的问题也不同,问错一套,就会在调研中自信地得出错误答案。痛点类问题指当下正在发生、造成某种代价的事情——它有一个可以衡量的“今天”。缺失类问题指现有产品缺少某种能力:没有人因此受伤,因为没有人在做这件事。构建类问题指仍在开发的事物中的下一块拼图——没有“今天”,没有替代方案,也没有可以询问的人,因为产品还未面向任何人。

大多数新功能属于第二类,但如果用第一类问题来审视,每个功能看起来都只是偏好。把前两类问题中的任何一套用于第三类,得到的答案都会是空白,于是这会被解读为工作依据不足,尽管这项工作从来就不是问题所在。

对于痛点:谁在受苦,要具体到你可以去和他们交谈;今天的代价是什么,用业务真正感受到的单位来衡量——分钟、工单、客户流失、退款、值班告警;如果一切不变会怎样,这能区分真正的问题和偏好,因为忽略起来毫无代价的问题,只是一个换了更好听说法的偏好。

对于缺失:谁今天无法做到这件事,然后是贯穿整个阶段的关键问题——他们用什么来替代。没有人会坐等软件。他们会用电子表格、手动流程、支持工单、竞争对手的产品,或者悄悄放弃,而你永远不会知道。替代方案就是证据:它可观察,在功能出现之前就已存在,也是“今天的代价”最诚实的对应项。接着问,如果永远不构建这个功能,什么会一直无法实现,以及谁会因此不断离开。

对于构建:锚点是承诺,而不是用户。承诺这部分内容要实现什么——路线图、推介材料、早期设计文档中是怎么说的。没有它,什么会停滞——不是对客户而言,而是对正在构建的东西而言:哪些模块无法启动,哪些内容会被临时替代,哪些东西会被重复编写。然后是让这类问题保持诚实的关键问题:为什么是现在,而不是等下一块拼图完成之后,因为在构建过程中,几乎所有东西迟早都值得做,眼下唯一真正需要决定的是先后顺序。

绝不要把一种问题的提问方式用于另一种问题。“如果我们什么都不做,会有什么问题?”在缺失类问题下,答案显而易见——什么都不会出问题,这正是缺失的含义;而在构建类问题下,真实的回答“项目会停滞”听起来像是在强行辩护,但实际上确实如此。把这两种情况当成调查发现,团队就会因此放弃所有可以构建的新东西。

要一针见血。含糊的回答不是用来记录的,而是要追问的:“用户感到沮丧”到底是哪些用户,在做什么,多久一次。要逐一质疑每个抽象说法,直到其中出现数字或具体名称,并在同一轮对话中完成,而不是先收集一堆含糊说法,留待以后再梳理。

没有最终用户时——例如重构、基础设施或平台工作——只是相关对象变了。值班人员会受影响,下一项功能会受影响,团队会受影响,而使用历程会变成运维流程:某人今天如何部署、调试、恢复。因为“这是内部工作”就跳过这些问题,只会让内部工作永远输给功能开发。

证据

在获取任何信息之前,先说清楚哪个数字会改变决策。然后去查那个数字。广泛收集信息的本能是错的:没人提出问题的仪表板只会白白消耗上下文,还会让文档变长,却不能让决策变得更好。

每个项目能获取的信息都不一样,所以要先探查,而不是想当然。产品分析、错误跟踪、日志、问题跟踪器、支持请求量——使用环境实际提供的任何信息,绝不要依赖其中某一项一定存在。最低限度是代码仓库,它始终存在。

缺失的数字不能证明任何事情。 它背后可能隐藏着两种截然相反的情况:我们测量过,结果很小,这是关于问题的发现;从来没人为此添加监测,这是关于监测机制的发现。某件未跟踪的事情发生的频率,就是它实际发生的频率;把这两者混为一谈,就会让一个未跟踪的问题被误读为小问题。

因此,数字缺失时,要询问,绝不推断。在场的人可以根据支持请求、销售情况和自己这一周的经历提供信息,这比朝任一方向默默假设都可靠。记录哪些事情无法测量,以及他们给出的信息,并分别标明其性质。让事情变得可测量有时是正确的第一步,但绝不是自动采取的步骤;而对于一项尚不存在的能力,也无事可测。

证据收集是访谈工作,不是文档的一个章节。 它的目的是确定结论;结论一旦确定,影响了结论的数字就应放进 Problem,写在读者理解问题时需要看到的那一行。单独罗列数字和非数字,是决策过程中的工作底稿;文档记录的是决策,而不是推理过程。如果要测量问题,需要一个尚不存在的事件,那就是一个切片。

用户旅程

梳理某人实际经历的完整流程,并向对方确认。不是画成图,也不是创建用户画像——这里的产出不是一张地图,而是重要状态以及每种状态下应该发生什么;规划工作的人需要这些信息,否则就只能自行编造。

旅程梳理要跟在证据收集之后,因为数字会改变哪些状态值得花力气讨论:如果几乎所有流量都走同一条路径,那么这条路径中的空状态、过期状态和放弃状态都值得认真讨论;另一条路径上的对应状态则只需一行说明。对每个分支都同样深入地梳理,会让旅程沦为无人阅读的地图。

任何真实流程都有比人们描述的顺利流程更多的状态:空状态、首次使用、重试、半途未完成、过期、未经授权,以及用户离开后第二天又回来的情况。去找出这些状态,将具体问题提给用户,并记录他们的决定。

这些是产品边缘情况:也就是应该发生什么。工程层面的跨领域关注点——并发、幂等性、授权、可观测性——不会在这里询问;它们要在之后依据代码做出决定,而且只有难以逆转的事项才会成为 Key decisions,其余则属于计划。界限很清楚:如果回答需要产品层面的意见,就在这里询问;如果需要查看代码仓库,就留到之后。

用户旅程在文档中没有专门的章节。 它作为产品问题在这里、结论确定之前进行询问;然后,每个确认过的状态都会落入回答该问题的切片中,放在该切片的状态表里,紧挨着调用方会看到的内容。把完整旅程保留在文档顶部,又把完整契约保留在每个切片中,意味着同一批事实以两种方式出现,规划者只能手动协调。没有任何切片涉及的状态,应在 Work index 下用一行指出处理它的现有代码。

界定本轮范围

说明本次探索要回答哪些问题、不会回答哪些问题,并在开始探索前取得共识。研究没有自然的终点,而此阶段之后需要针对每个选项阅读代码,这才是耗时的部分。如果不预先商定边界,“理解问题”就会变成对整个代码仓库进行全面审查。

比构建它更便宜

在下结论之前,先在这里拓宽一次解决方案空间。上一个阶段从某人拿着来到你面前的解决方案中还原出问题,而此时很容易把同一个解决方案交给技术阶段,后者会尽职尽责地给出两种形式。错误的解决方案有两种形式,仍然是错误的解决方案——这是这项技能最不容易察觉的失败,因为每一步看起来都像是在取得进展。

所以,想想怎样才能消除你刚刚衡量出的成本,而不必构建提议中的东西。更改默认设置。删除这一步,而不是为它提供支持。调整措辞,从源头避免混淆。先手动处理前 10 个案例,这通常能让你弄清楚该构建什么。解决承担大部分成本的那一半,剩下的暂且不管。直接购买。

购买是唯一需要向外查看的选项,而且范围有限:是否已有适用于此需求的产品,以及它的大致成本与所能替代的几周工作相比如何。这不是供应商调研,也不违反规则 1——指出某类产品存在,并不等于提议采用某种模式。至于如何设计方案,留待“先例研究”阶段。

大多数选项一行就能否决,这正是目的——让用户看得到它们并加以考量,使成本高昂的路径成为一种选择,而非默认选项。有一个选项得以保留时,这就是发现阶段回报最高的时刻。而且,这件事要么在这里发生,要么永远不会发生:下结论之后,工作便已确定其形式和成本;在比较如何实现时,没人会重新审视该构建什么。

结论

如果决策已经敲定,这部分就是记录,而不是关卡:用一行写明由谁在何时敲定,然后直接进入方案。只有一种范围明确且确实存在的例外——如果问题阶段的工作与该承诺相矛盾,就直截了当地说明并停下来,因为这是值得打断既定计划的发现。除此之外,不要再为已有的决策设置关卡。

如果决策尚未敲定,就有四种结果,而且四种都切实可行:构建、构建规模更小或有所不同的方案、暂不构建、不构建。能够说出后三种结果,正是第一种结果有价值的大部分原因。

在施工阶段,结论关乎先后顺序,而非是否存在。对于项目围绕其设计的某个部分,“不构建”很少是真正的选项;“暂不构建,等 X 之后再说”则往往是,并且同样需要说明:等待期间哪些事情会停滞,之后再做会导致哪些内容被写两遍。

第二种结果暗藏陷阱。“规模更小或有所不同”意味着提出了一个新方案,因此问题部分仍须适用于它:说明它消除了已衡量成本中的哪一部分,又留下了哪一部分。如果省略这点,一个针对稍有不同的问题、成本更低的答案,就会因为更便宜而获批。

不做也是一种押注,也要像其他选择一样论证自身。 负担会自然倾斜:构建必须拿出证据,而不构建则会因为没人衡量过任何东西而成为默认选项。这是在天平上施加砝码,并非中立。因此,“暂不构建”和“不构建”都要说明自身依据——维持现状的代价,以及改变答案需要满足什么条件——而且要用与实施成本相同的单位来表达。如果一方用工程师周数衡量,另一方却只用形容词描述,那么形容词每次都会落败,而形容词所描述的通常是客户。

用问题阶段确定的术语说明结论及理由——比较什么都不做的成本和做点什么的成本。然后停下。在讨论任何技术选项之前,必须先由用户确认。这才是关卡;如果你自己越过它,那么之前的一切都毫无价值。

结论在对话中是一道关卡,在文档中则只占一行。 由标题中的 Status 表示:confirmed by <who>, <date>,或附上理由的 declined,除此之外不再用其他内容表示。推理过程、考虑过的低成本路径,以及权衡过的四种结果,都是决策过程中的工作内容;它们留在对话里,需要了解这些内容的读者可以去找确认决策的人。Verdict 部分若重述标题中已有的内容,只会让文档变长,却不会让决策更明确。

什么才算有效

结论是一场押注,而无人兑现的押注,只是一个带日期的观点。因此,在讨论任何技术问题之前,先说明怎样才算值得做这件事——用 问题本身的单位,也就是 Evidence 确立的那个单位。如果代价是退款,成功就以退款衡量;如果代价是人工操作的分钟数,成功就以分钟数衡量。在这里更换单位,押注就会悄悄变得永远不会输。

“已经发布”不代表成功,那只是你早已同意要做的事。 这种偷换是这套方法中最常见的失败,之所以让人安心,恰恰是因为它有保证:功能上线只是产出,无论有没有人因此解决了问题,这一点都成立。任何只会不断增加的数字也是如此。

三行就能说清。有效的标准——预期结果,以及大致何时达成。早期信号——几天内而非几个季度后就能看到的迹象,以及押注出问题时会是什么样子,目的是趁采取行动成本还低时及时应对。复盘——一个日期或触发条件,以及由谁查看;没有这些,就永远不会有人检查,而复盘是人们从做出决定中吸取教训的唯一时刻。

利害不大时,只写第一行即可。对于无人会再回头审视的改动,写上复盘日期只是一种仪式,它会让人们养成习惯,在真正需要复盘的改动上也跳过这一节。

如果结果无法衡量,就沿用 Evidence 的表述方式作答——未跟踪不等于影响小——然后诚实地从两条路中选一条。指出一个并非指标、但可观察的替代信号:电子表格不再更新、支持帖不再反复出现、手动步骤从运行手册中移除。或者指出需要添加哪些监测,以及这是否属于本次工作。在 构建 阶段,替代信号应当具备结构性且可核查——等待区块可以启动,而且被设为存根的内容没有缺失。

如果衡量它需要一个当前不存在的事件,那么这属于构建工作,应纳入计划;这是本节中规划者唯一需要转化为任务的内容。这里的其他任何内容都不应成为验收标准:“退款减少三分之一”无法通过合并时的测试来证明,把它作为下游条件,要么会阻碍正确实现,要么会被弱化到轻易通过。

如果结论是 暂不处理 或 不构建,则将文档状态设为 declined,并写明原因、Situation、Problem 和 Boundary——需要满足什么条件才能重新考虑,应写入 Boundary。不要为你刚刚否决的工作虚构 Shape 或切片。

决策

现在打开代码库,并且要在提出任何方案、阅读任何外部内容之前打开它。一个选项不是设计;它是设计加上它在 这里 的成本。同一个选择在一个代码库中显而易见,在另一个代码库中却可能荒谬。先提出方案、再检查代码库,意味着你是在为立场辩护,而不是形成方案;先读基准测试,意味着你会戴着别人的眼镜审视自己的代码库。

仓库告诉你的信息

变更频率不会告诉你哪个选项正确,只会告诉你选错的代价有多高。 这是判断稳健性的依据,也是最有用的信号,因为 git 始终可用。一个每年只修改两次的文件里,有一条单向门,即使选错,代价也很低;而在每周都会修改的文件里,同一扇门就像一张每周寄来的账单。如果答案已经是“代价很低”,就用一条命令查明,然后继续——而一个只有三个月历史的项目,也无从查起。

变更频率的含义可能两面皆有,所以绝不能孤立地看。变更频率低,意味着稳定、被弃用,或者痛苦到所有人都绕着走;变更频率高,意味着业务的关键路径,或者一块从未妥善拆分的部分。两个低成本信号可以帮助区分:有多少不同的作者会修改它,因为作者越多,修改它就越像一项协调成本,而不只是编辑;以及它最近是否仍在变动,因为一个上线六个月后仍在变化的区域,还没有定型,现在不值得抽象。

也要了解约定,并把它们看作优先级,而不是需要权衡的成本。当这个代码库已经回答了你面前的问题时,除非你能说清它哪里错了,否则就应遵循它的答案——不是说替代方案哪里更好。一个外部方案如果只是与既有方案打平,就应该落败,因为平局会按哪个方案写起来更有意思来决定,而这正是产品线如何一次次因看似合理的改进而逐渐偏离方向的。

先例

总有人解决过类似的问题,相比在生产环境中摸索,了解他们的做法成本更低。这里也适用对数字的那种严谨态度:在查找之前,先说清楚基准案例会改变什么,否则“别人是怎么做的”只会返回三段关于一家与你无关的公司的介绍。

有三点值得借鉴。不同团队反复采用的模式,因为趋同是一种证据——各处都出现的同一条分界线,通常是承重结构,而不是一时风尚。每个人都提到的失败案例,这是这项技能中成本最低的一项:通过阅读而不是发布来避开边缘情况。还有哪些东西最后证明并不必要,只有团队描述自己移除了什么,你才能知道这一点。

规模永远无法照搬,而这正是本节可能造成伤害的地方。 一家流量是你一千倍的公司发布的设计,忠实反映的是他们的约束;照搬它,就等于为一个你并不存在的问题买单。如果基准案例主张采用更重的方案,就要指出你与他们有哪些共同条件;如果一个都没有,那它虽有意思,却无关紧要,而明确指出这一点就是结论。要读设计背后的约束,而不是设计本身。

如果找不到可比案例,这本身也是一种结果:问题很特殊,或者问题的界定方式不对,以致没人会用这种方式描述它。后一种情况值得回头重新审视。

如果选错的代价很小,就跳过本节。 如果在场的每个人都能想象出同一种模式,就不必做文献综述;仍然做,反而会变成这里本想避免的凑字数。也不能假设一定能访问网页;如果无法访问,就明确说明,不要凭记忆推断某个产品如今的做法,因为这正是知识链所禁止的凭空捏造约束。在 Sources 中引用你实际读过的内容——Sources 只收录研究所依据的文档:路线图、决策记录、早期设计文档和外部参考资料。代码路径应在正文中就近引用,紧挨着它们所佐证的事实,不要在 Sources 中重复。

两种方案

提出整体方案,而不是分别讨论每项决策。八项决策,每项两个选项,就是十六个选项,读者会看得精疲力尽;真正重要的是那个有实质分歧的整体方案,较小的决策一行就能说明白。

第一种方案显而易见:它是现有背景所暗示的方案,价值与投入的比率最高,也是一个务实的团队会交付的方案。第二种方案经得住考验:它更经久耐用,但现在成本更高。两者的分野始终相同——现在付出,还是以后付出——明确指出这一点,比较才算诚实;也能避免把第二种方案说成只是不同风格,而是明确指出它代表另一种押注。

说明每种方案在什么条件下胜出。这样别人才能有建设性地提出异议:不是“我更喜欢另一种”,而是“你的条件不对,我们每个月都会改动那个部分”。

较轻量的方案承载推荐意见,较重的方案必须说服读者放弃它,而选择较重方案也是正常结果,并非意外。默认选择较轻方案,是因为支持较重方案的条件,是对未来的判断;未经核实的未来判断,正是团队今天为永远不会到来的规模付出代价的原因。所以,把条件写成现在时并加以核实:不要写“我们可能需要第二个区域”,而要写“我们目前只在一个区域运行,路线图上也没有增加区域的安排”。

条件成立时,就明确说明并切换方案。“Situation”已经收集了相关证据——路线图承诺、正在进行且需要此方案的工作,或今天就已存在的第二个使用方——所以“可信地近在眼前”必须是清单上的事实,绝不能只是一种感觉。清单中有一项符合条件时,现在交付较轻量的方案,就意味着之后要把东西再做一遍。

做得太少同样会自我辩护,代价也各不相同。过度构建会浪费一笔看得见的钱;构建不足则会带来重写成本,以及重写之前一路上造成的损失,而两者都不会计入估算。因此,推荐较轻量方案时,要说明它无法应对什么情况,以及什么情况会迫使我们重写。若说不出其中任何一项,你就没有比较两种方案——你只描述了一种,并默认选择了它。

两种方案之外的选项属于补充视角,并须标明这一点(规则 7)。如果现实中有三种做法,但其中两种不在考虑范围内,那么逐一列出三种做法,并用一句话说明排除每种做法的理由,要好过悄悄地只呈现留下的方案:这样,“这是我的答案”就变成了“这是可选方案,以及范围如何缩小”。每种做法各用一行,绝不放在“Decisions”中,也绝不为了让推荐显得势在必行而添油加醋——规则 3 禁止的稻草人论证,指的正是这种做法。

如果第二种方案并非真正可选,就不要为它单独设一节。一句话——“只有预期达到 N 时,较重的方案才值得采用,而我们并无此预期”——就能完整呈现比较,并把被否决的方案及其致命条件交给规划人员。

方案说明最多四句话:当前记录是什么、操作流程如何运行、入口在哪里以及更改它的成本是什么,还有带有适用条件的较重方案。不要说明它遵循哪些层——仓库规则已经规定了这些,重复说明只会让框架泄漏到文档中;只有设计偏离这些规则时,才值得单独用一句话说明。不要提及先例——“Situation › In flight”已经做过了。如果“Shape”提到了某种方法,那就是实现细节;如果它重复说明了某项关键决策,那就是赘述。

文档包含什么

文档只包含基本决策——产品、API、架构、方向——以及计划无法从这些决策推导出的内容。 三条规则让文档保持精简,并且会在撰写文档前检查,而不是之后:

  1. 每项决策只陈述一次,放在对其负责的最高层级。 关键决策是所有难以撤销的事项的归属地;一个切片只需写「关键决策 2」,然后继续。相同事实同时出现在形态、关键决策、切片表格和图示中,不是四种视角——而是一项决策让读者遇见四次,维护者还要在三处更新。一个将十项决策阐述八十次的设计,读起来会显得臃肿,即使每个章节单独看都似乎有其合理性。
  2. 标识符的上限:路由、表、列、类、术语表术语。 更低层级的内容——方法、容器令牌、文件路径、CSS 选择器——都不应出现在设计中。如果计划无法根据路由和类名确定方法,那是计划的问题;而设计一旦写出方法,就已经开始规划,并会在计划发挥作用时立即与计划脱节。
  3. 文档说明将会存在什么,而不是将会执行什么。 状态表、契约和架构定义一个切片。要添加的文件和要修改的方法清单是计划的产出;包含这种清单的设计,就是披着设计标题的任务列表。
切片

设计按垂直切片组织,绝不按视图类型组织。一个切片是一个可独立安排人员的垂直单元——记录、改变该记录的操作、命名新子项的父项、屏幕——并且在一处包含读者理解它所需的内容:它交付什么、它的状态、它的状态表,以及仅限它确实有内容的其他视图。想了解某项操作的读者只需阅读一个章节。把每个流程都放在「流程」下、每张表都放在「架构」下、每项决策都放在一张表中的文档,是为作者而非读者组织的;读者要跑五个章节才能拼出一个切片,最后只会放弃。

先划分切片,再填充内容。 通常为五到八个。只有一个切片时,只需一个章节,不需要索引。「支付发票」是一个切片;「编写服务」是一项任务,而任务属于规划。索引是一张表,列出切片、完成后会存在什么、状态,后面再接一行顺序说明。文档中的所有模糊之处都会体现在这一列状态中:clear、open(已采用默认值)、rfc、spike、design。

切片名称必须是领域术语或代码标识符,不能是描述性动词短语。 「发票」和「支付发票」指的是读者可以搜索的事物——表、路由。「账单」和「结算」在代码库或术语表中都找不到对应内容,而不在场的读者首先会问它们是什么意思。正文也要遵循同样的规范:每次都用术语表中的名称称呼记录。为同一事物使用第二个名词——无论句子读起来多么自然——都是术语表所禁止的同义词,而设计文档正是它最先渗入的地方。

一个切片由它的状态表加上只有它能说明的内容构成。 状态表列出该切片负责的每个旅程状态、应当发生什么、调用方会看到什么;它既是切片的验收标准,也是一种检查方式:用户确认过的每个状态都必须在某个切片中有一行,或者在索引下方用一行说明它目前由什么处理。访谈中确认过但每个切片都没有提及的状态,是功能做得太少的具体表现,也是唯一能通过阅读而非发布发现它的方式。不要列「新增/变更」:计划会根据契约、架构和流程推导出文件和方法。每个切片也不应有决策表:代价高昂的内容放在关键决策中;可逆且值得读者提出异议的内容,只需一行「考虑过的替代方案」——列出选项,以及该选项胜出的条件。 可推导的约定只在索引下命名一次,不在每个切片中重复。 复制文本、截断方式、排序顺序、400 响应的确切句子、哪些代码会获取正文引用和路径 ID——“全部按 Invoice 的做法”,并附上记录该约定的位置。把它们放进各个切片,会让真正重要的七项决策淹没在二十三项无关紧要的细节中。

关系或顺序本身就是内容时画图,否则用文字说明。 每种视图只出现在包含相关内容的切片中:

  • Schema——切片创建或修改表的地方:用列出名称、类型、是否可空、引用和备注的列名表格,另列索引,以及现有表的变更。不写代码:构建者知道如何将列名表格转换为 ORM 调用。涉及多个记录时,绘制实体图,并对照代码检查。某个切片修改、但由另一个切片创建的表,应放在创建它的切片中。
  • 流程——切片包含单向门槛的地方:绘制序列图,参与者为各层而非文件;步骤包括任何读取发生前的拒绝、可能失败的读取、事务,以及必须能干净处理的竞争。不包括截断方式和存在性检查——这些属于计划内容。每个门槛一张图,不按状态码分别绘制。
  • 状态——只有超过两个状态或存在多个写入方时才需要。两个状态和一次转换,用一句话写在关键决策中即可;用图表示它们,不过是给句子配图。
  • 契约——切片新增的每个端点占一行:方法、路径、请求结构、成功响应结构。失败代码已列在状态表中,与其编码的结果相邻。
  • 待处理事项、RFC、试验、设计——放在它们所阻塞的切片中,并由切片的状态注明具体是哪一项。不存在全局交接章节。

关键决策置于索引上方,以散文形式列出五到八项:无论相关内容位于何处,都要说明难以撤销的事项。读者读到这里停下时,也应能了解设计。若超过八项,说明设计尚未成形。每项都应是一个不变量或位置选择,绝不能是实现机制——“记录 Payment 并将 Invoice 标记为已付款必须是原子操作;竞争失败时不留下孤立记录”是一项决策;实现它的 SQL 则不是。

迁移和发布只出现在 Situation 表明产品已投入使用的地方:现有行、数据回填、部署顺序、旧客户端在更新前会看到什么。在建设阶段,写一行说明即可,也可以不写;在稳定使用阶段,这里是技术风险所在。忽略这一部分的设计,只估算了代码成本,没有估算变更成本。

每张图落地前都要对照仓库检查。调用不存在方法的序列图、基数错误的实体图、代码无法到达的状态——这些都是披着图示外衣的虚构约束,而人们相信图示的速度比相信文字快。默认使用 Mermaid,因为它支持差异比较,并能在文档所在位置渲染;设计交接中绘制的屏幕图则应链接引用,绝不重新绘制。

不变量,而非机制。 “每个 Invoice 只有一个 Payment,竞争失败时不留下孤立的 Payment”是一项决策——它是产品层面的结果;如果规划者只看到唯一索引,就无法推导出所需的原子性。“条件 UPDATE ... WHERE status = 'open',影响零行就回滚”是一种机制——满足该不变量的多种方式之一;点名它会排除其他方式,还会把 SQL 放进一份禁止出现方法名的文档中。设计应陈述不变量;如果显而易见的先例会违背它,就要明确说明——“Refund 会分别写入状态和账本条目;此处不要照搬”是规划者无法自行推导、否则就会做错的说明。检验标准是:称职的规划者只读不变量,能否独立得出这里的做法,并得出同一个做法?如果可以,那是他们该决定的事。如果先例会引向错误方向,警告就是你该补充的内容。 代码几乎不会出现在这里。 ORM 完整表结构、服务方法、路由处理程序、SQL 谓词,都应由构建者根据列明细表、流程和契约编写;把它们放进设计中,会让文档变成偏离代码的东西,而不是代码据以校验的东西。少数例外是那些本身就是产品决策的字面值——枚举值、路径、响应形态——它们已经记录在状态表、契约和架构中。

何时停止决策

两个维度可以解决所有剩余问题,而这个表格就是停止规则——当每个问题都落在四个格子之一时,你就完成了,而不是等到精疲力尽才停下。

明确不明确
影响大做出决定。记录方案、备选方案,以及什么情况会改变决定。不要做决定。 这需要 RFC;如果只有构建才能给出答案,则需要一次探查。
影响小用一行记录。采用合理的默认方案,注明这一点,并在构建过程中做出决定。

右上角的格子最容易被违反,因为做决定让人觉得是在取得进展。一个影响重大的选择,如果没人能看清楚,就恰恰不该在讨论中敲定;将其提交为 RFC,意味着探索正在发挥作用,而非以失败告终。

当探查优先于 RFC:缺失的是只有通过构建才能获得的知识,例如服务提供方是否会有这种表现,或这种方案在当前规模下的性能如何。写明它要回答的问题、不同答案会带来哪些变化,以及何时停止——没有停止条件的探查就会变成实现本身。

需要设计是第三种交接方式,也是最容易被忽略的一种。在 API 背后,探索阶段做出决定后就直接进入规划;但对于屏幕界面,必须有人在中间绘制出来,而计划会假定这一步已经完成——它会链接到视觉稿,而不是凭空设计。因此,如果屏幕在设计完成前无法规划,就要明确说明这一点,以及设计必须解决什么问题:涉及哪些屏幕,以及需要涵盖哪些旅程状态。然后,图稿应放在本文档中——在 Sources 中提供链接或路径,并在该切片的状态表中引用——而不是放在规划者不会打开的配套简报或 PDF 中。

之后,表格中的每个格子都要落到某个切片中:已做出的决定放在其 Decisions 表中;RFC、探查和设计则作为其状态和待解决问题记录。Work 索引通过单个状态列展示整个表格。如果读者无法仅凭这一列看出有哪些工作以及哪些内容仍不明确,那么索引就没有做好。

每做出一个决定,就立即记录,而不是留到最后。事后补写的决定需要重新推导;而重新推导正是理由悄然变成事后合理化的地方。

格式

撰写 .design/<name>.md 工件时,阅读 references/document-format.md——在确认结论后,或记录已承诺的决定后。访谈期间不要加载它。该模板中的章节标题都是固定文本:读者和工具会按名称查找它们。

知识链

严格按顺序:现有代码和约定、项目文档、库文档、网络搜索,然后标记为不确定。绝不要编造 API、限制或行为。“我找不到相关文档”胜过看似合理的捏造——而在这里,编造出的约束不会导致错误,它会排除原本可行的选项。

输出

产出工件——除非 Situation 得出的结论是这里没有需要探索的内容,此时产出该段文字。不要叙述阶段过程。访谈规则约束提问;在这里,应明确陈述结论,并删去填充语和模棱两可的措辞。

示例

示例 1:尚未决策

用户说:“我们应该为仪表板添加缓存吗?” 操作:

  1. 查明情况(已发布的产品、正在进行的工作)。仅询问仓库无法确定的信息。
  2. 从“我们需要缓存”中还原问题——谁受影响、目前付出了什么代价、如果不做任何改变会怎样。
  3. 明确哪个数字会改变决策,然后获取这个数字。
  4. 梳理相关的旅程状态。提出比自行构建更便宜的方案。
  5. 给出结论后停下,等待确认。
  6. 确认后,阅读仓库,再提出两种方案,然后拆分切片,并用每个切片的差异、流程、架构、契约和决策填充内容。根据 references/document-format.md 编写 .design/<name>.md。 结果:一份设计文档,包含标题中的状态、情况、问题、成功标准、边界、方案、关键决策和工作——其中工作部分是带状态的切片索引,随后每个切片各有一个章节,并附带各自的状态表;或者一份在边界部分后即结束的 declined 文档。
示例 2:已经做出决定

用户说:“我们已经决定构建计费功能。帮我确定方案。” 操作:

  1. 在情况部分记录已做出的决定。不要重新启动结论评估。
  2. 访谈仍会影响方案的旅程状态和证据。
  3. 提出方案前先检查仓库。给出两种方案及其成本。根据关键路径流程、状态机和架构进行设计。编写 .design/<name>.md。 结果:结论行写明“已决定——见情况”。方案、关键决策和每个切片都已填写。任何人都可以据此规划,无需参考对话内容。
示例 3:技能不适用

用户说:“把这份设计文档拆分成任务。” 操作:不要运行此技能。规划应从文档开始;此技能负责生成文档。 结果:转交处理;此技能不会生成 .design/ 文件。

常见问题

第二轮就匆忙得出结论

原因:在得出结论前就提出了解决方案,用户说了“好”,这就被当成了决定。 解决方案:停下。还原问题。如果已经做出决定,就记录下来并跳过决策关卡——不要再人为制造一个新决策。

新功能的案例内容空洞

原因:针对痛点的问题(“如果我们什么都不做,会有什么问题”)被用于功能缺失或构建问题。 解决方案:切换问题类型。功能缺失:他们现在用什么替代方案。构建问题:什么事情受阻,以及为什么现在要做,而不是等下一个环节完成后再做。缺少某个数字,不代表问题很小。

描述或模板占位符泄漏到产物中

原因:复制格式模板时,保留了尖括号提示。 解决方案:将每个占位符替换为具体值,或省略相应章节。标题下写着“N/A”的章节不应出现。

用设计文档的形式包装规格说明

原因:一个“决策”表扩展到三十行,列出了错误文案、裁剪方式和排序规则,而真正决定设计的事务只占其中一个单元格。没有流程,没有状态机,架构只写了一行。 解决方案:将难以撤销的决策以文字形式提取到“关键决策”中,在负责该设计的切片内绘制关键路径,并以列式表格编写架构。删除可以推导出的字面值——规划者可以从仓库已有的约定中获取这些值,并在索引下引用该约定一次。

按视图而不是按切片组织内容

原因:所有流程都放在“流程”下,所有表格都放在“架构”下,所有端点都放在“契约”下,所有选择都放在一个“决策”表中,所有待解决问题都放在末尾。每个章节都井然有序,但想了解某项工作内容的读者却要翻阅五个章节。 解决方案:先拆分切片。每个切片都包含自己的状态表、流程、架构、契约和待解决问题。切片上方只保留核心方案、关键决策,以及每个切片各含一个状态的索引。

一个决策说了八十遍

原因:这笔交易出现在「情境」「既有工作」「形态」中两次、两个「关键决策」、切片的「交付内容」、状态表、图示、图示后的段落、横切说明、决策行和架构说明中。每个部分都因「有内容」而保留下来,而这笔交易处处都有内容。 解决方案:每个决策只说一次,由最高层级的相关部分负责。「关键决策」中保留该内容;切片写「关键决策 2」;图示中呈现该内容;其他地方不再提及。然后移除每个切片的决策表、横切说明以及「新增/变更」列表——它们都是重复内容的载体。

写进设计里的计划

原因:每个切片都列出要修改的文件、容器令牌、方法名、测试套件中 DELETE FROM 的顺序,以及屏幕上的联合类型。 解决方案:设定标识符上限——路由、表、列、类、术语表条目。超出这些范围,就属于计划。设计说明将会存在什么;计划说明将要做什么。

无人核对的图示

原因:实体图示称每条账本记录都有一张 Invoice;代码却表明 Refund 和 Adjustment 记录都没有 Invoice。 解决方案:每张图示在提交前都要对照代码仓库核查。人们相信图示的速度比信任文字更快,这也正是错误图示代价更高的原因。

评论

支持 Markdown