product-thinking
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseProduct Thinking
产品思维
A runbook for changing anything a user interacts with. The agent defaults to building exactly what was asked; this pass adds the product checks it skips — whether the change earns its permanent place on the surface, and how you'll know it worked. Work the steps before you write code, fill in the brief as you go, and fold that brief into the PR. Use it to push back when a change isn't justified, not just to wave one through.
这是一份针对用户可交互内容变更的操作手册。Agent默认会完全按照需求构建功能;而本流程会补充它忽略的产品层面检查——该变更是否值得长期保留在交互面上,以及如何验证它是否有效。在编写代码前先完成这些步骤,过程中填写简要说明,并将其纳入PR中。当某项变更缺乏合理性时,用此流程来驳回,而非仅仅放行。
When to run it
适用场景
Run the pass whenever a diff touches a surface a user — a developer running React Doctor, or their CI — depends on. Every surface is a contract: once it ships, people build on it and you can't quietly take it back. Find where the surface lives first, so you edit the right place and can search it for something to reuse before adding.
| Surface | Where it lives | Why it's load-bearing |
|---|---|---|
| CLI command / flag | | Every flag is forever — it has to be parsed, documented, tested, and kept working across releases. |
| 0–100 score | | The number people screenshot and gate CI on; changing the math silently moves every user's score. |
| Config option | | A long-lived contract that interacts with existing precedence; a new knob is rarely removable later. |
| JSON report field | | A wire format other tools parse; a shape change breaks integrations unless |
| Package API / exit code | | Programmatic consumers pin to it, so changes are semver-breaking and need a changeset. |
| GitHub Action input/output | | Versioned independently ( |
| Terminal output / UX | | The first impression and the daily experience; noise or confusion here is what makes people stop running it. |
Not here: lint rules go through the → → pipeline, and rule configuration through — this pass is for the surface around the rules, not the rules themselves. Internal-only changes (the engine, private types, tests, tooling) skip the pass entirely; note in one line why the change is internal and move on.
rule-researchrule-writingrule-validatedoctor-explaincore只要变更涉及用户(运行React Doctor的开发者或其CI系统)依赖的交互面,就需要执行本流程。每个交互面都是一份契约:一旦发布,用户就会基于它构建功能,你无法悄无声息地撤回。首先要找到交互面的所在位置,这样才能编辑正确的代码,并在新增内容前搜索是否有可复用的现有方案。
| 交互面类型 | 所在位置 | 为何至关重要 |
|---|---|---|
| CLI命令/参数 | | 每个参数都是长期存在的——它需要被解析、文档化、测试,并在各个版本中保持可用。 |
| 0-100评分 | | 用户会截图该分数并用于CI门禁;修改评分逻辑会悄无声息地改变所有用户的分数。 |
| 配置选项 | | 这是长期存在的契约,会与现有优先级交互;新增的配置项很少能在后续移除。 |
| JSON报告字段 | | 这是其他工具解析的有线格式;除非处理好 |
| 包API/退出码 | | 程序化消费者会依赖它,因此变更属于语义化版本的破坏性变更,需要添加changeset。 |
| GitHub Action输入/输出 | | 独立版本化( |
| 终端输出/用户体验 | | 这是用户的第一印象和日常使用体验;此处的冗余信息或困惑会导致用户停止使用该工具。 |
不适用场景:lint规则遵循→→流程,规则配置通过处理——本流程针对的是规则之外的交互面,而非规则本身。仅内部变更(引擎、私有类型、测试、工具)可跳过本流程;只需用一行说明为何该变更属于内部变更即可继续。
rule-researchrule-writingrule-validatedoctor-explaincoreSteps
步骤
- [ ] 1. Write the job + the change (2 lines)
- [ ] 2. Search for a reuse candidate; extend it or justify new surface
- [ ] 3. Wire exactly one metric
- [ ] 4. Pick the default + add the compatibility artifacts
- [ ] 5. Update docs
- [ ] 6. Record the kill metric
- [ ] 7. Run checks- [ ] 1. 撰写任务描述与变更内容(2行)
- [ ] 2. 寻找可复用的候选方案;扩展现有方案或说明新增交互面的必要性
- [ ] 3. 对接恰好一个指标
- [ ] 4. 选择默认行为并添加兼容性构件
- [ ] 5. 更新文档
- [ ] 6. 记录淘汰指标
- [ ] 7. 运行检查1. Write the job and the change
1. 撰写任务描述与变更内容
Write two lines before anything else — they anchor every later step:
- Job — who runs React Doctor, what they are actually trying to get done, and what they do today instead (an existing flag or config option, a different tool, or nothing at all). Pin the job to behavior you've observed, not to what someone says they would hypothetically want.
- Change — the smallest surface that does that job. If the change is bigger than the job, you are building speculatively.
If you can't name a job the current surface can't already serve, stop — there is nothing to build, and the right move is to point the user at the existing way to do it.
Then run two filters before you commit to building:
- Durability — will this still matter as the model gets better at React (or the target framework)? Capabilities the model will absorb, or that are trivially commoditized, are not worth a permanent slot on the surface. Spend surface area on durable, deterministic value.
- Trust — a feature users don't trust is worse than no feature, because it teaches them to ignore the tool. If a change can fire false positives or can't back its recommendation with evidence, fix the trust problem first; shipping it "mostly right" erodes the whole product.
在开始任何工作前先写两行内容——它们会锚定后续所有步骤:
- 任务——谁在运行React Doctor,他们实际想要完成什么,以及当前的替代方案(现有参数或配置选项、其他工具,或无替代方案)。任务要基于你观察到的实际行为,而非用户假设的需求。
- 变更——能完成该任务的最小交互面。如果变更范围大于任务需求,那就是在进行投机性构建。
如果你无法指出当前交互面无法满足的任务,就停止——没有需要构建的内容,正确的做法是引导用户使用现有方式完成需求。
然后在决定构建前运行两个筛选器:
- 持久性——随着模型对React(或目标框架)的支持越来越好,该功能是否仍有意义?模型会吸收的能力,或容易被同质化的功能,不值得长期占据交互面。应将交互面留给持久、确定性的价值。
- 信任度——用户不信任的功能还不如没有,因为它会让用户忽略整个工具。如果变更可能产生误报,或无法为其建议提供证据,先解决信任问题;“大致正确”地发布功能会损害整个产品的信任度。
2. Search before adding
2. 新增前先搜索
Reuse beats adding every time — the cheapest surface is the one you never create. Before adding a flag, option, or report field, search for an existing one to extend:
bash
bunx @rayhanadev/truffler "<name or behavior>" packages --kind function,interface,type,constant --limit 20
rg -n "<flag-or-option-name>" packages/react-doctor/src/cli packages/core/src/typesRead the top matches before writing anything. Extend an existing surface when it does almost the same job — a new value on an existing flag, a new field on an existing config object, an extra column on output that's already there. Only add a brand-new surface when the job is genuinely distinct and no existing entry can carry it without becoming confusing. If you do add one, record in the brief what you searched for and why nothing fit, so the next person inherits the reasoning instead of re-litigating it.
复用永远优于新增——最廉价的交互面是你从未创建过的那个。在添加参数、选项或报告字段前,先搜索是否有可扩展的现有方案:
bash
bunx @rayhanadev/truffler "<name or behavior>" packages --kind function,interface,type,constant --limit 20
rg -n "<flag-or-option-name>" packages/react-doctor/src/cli packages/core/src/types在编写任何代码前先查看顶部匹配结果。当现有交互面几乎能完成相同任务时,扩展它——比如为现有参数新增值、为现有配置对象新增字段、在已有输出中添加额外列。只有当任务确实独特,且现有方案无法承载而不产生混淆时,才新增全新的交互面。如果确实需要新增,在简要说明中记录你搜索了什么,以及为何没有合适的方案,这样后续人员就能继承你的推理,而非重新争论。
3. Wire exactly one metric
3. 对接恰好一个指标
You can't manage a surface you can't see, so a change isn't done until it emits a signal. Decide that signal before you build — if you can't describe which number would move, you don't yet know what success means. CLI telemetry is anonymized Sentry and is already a no-op for the library, tests, and , so all you wire is the emit:
@react-doctor/api--no-score- Adoption count — is anyone using it? Add a name to the map in
METRICand emit it viacli/utils/constants.ts. Use this for "did people turn this flag on".cli/utils/record-metric.ts - Per-scan outcome — what happened on this run? Add an attribute to the wide event in (a run-level dimension goes on
cli/utils/build-run-event.ts). Prefer one rich wide-event attribute over a pile of narrow counters — it stays queryable after the fact without new code.cli/utils/build-sentry-scope.ts
Anonymization is non-negotiable: never put a username, path, secret, or repo identity in an attribute — everything must ride / . Then write the metric name and the threshold that would mean "this worked" into the brief.
scrubSentryEventscrubSentryMetricNorth-star to judge against: the one durable metric is CI cohort retention — are teams still running React Doctor in CI weeks later? Treat most other numbers as vanity. For "is this loved rather than merely used," the proxy is a PMF survey scoring ≥40% "very disappointed" if it went away.
你无法管理看不到的交互面,因此变更完成的标志是它能发出信号。在构建前就确定该信号——如果你无法描述哪个数值会变化,说明你还不知道成功的定义。CLI遥测是匿名的Sentry,且在库、测试和模式下默认禁用,因此你只需对接发射逻辑:
@react-doctor/api--no-score- 采用计数——有人使用它吗?在的
cli/utils/constants.ts映射中添加一个名称,并通过METRIC发射。用于统计“是否有人启用了这个参数”。cli/utils/record-metric.ts - 每次扫描结果——本次运行发生了什么?在的宽事件中添加一个属性(运行级维度添加到
cli/utils/build-run-event.ts)。优先使用一个丰富的宽事件属性,而非一堆窄计数器——这样无需新增代码就能在事后查询。cli/utils/build-sentry-scope.ts
匿名化是不可协商的:永远不要在属性中包含用户名、路径、密钥或仓库标识——所有内容都必须经过/处理。然后将指标名称和代表“成功”的阈值写入简要说明。
scrubSentryEventscrubSentryMetric判断的核心指标是CI群组留存率——团队在几周后是否仍在CI中运行React Doctor?将大多数其他数字视为虚荣指标。对于“用户是否喜爱而非仅仅使用”,替代指标是PMF调查中“如果该功能消失会非常失望”的比例≥40%。
4. Pick the default and add the compatibility artifacts
4. 选择默认行为并添加兼容性构件
The default is a product decision, not an implementation detail — most users never change it, so the default is the behavior for almost everyone. Pick one that preserves today's behavior and make anything risky opt-in. Then ship the artifacts that keep existing users from breaking:
- Published-package behavior changed → add a changeset () so the change is versioned and lands in the changelog.
nr changeset - JSON report shape changed → edit , preserve the existing shape or bump
core/src/schemas.ts, and runschemaVersion. CI pipelines parse this output, so an unannounced shape change breaks them silently.nr smoke:json-report - Score weight or algorithm changed → every user's number moves at once and CI gates keyed to a threshold can flip. Treat it as a breaking change: changeset plus an explicit call-out of the shift.
- changed → cut a
action.ymltag and move the floatingvX.Y.Z, because other repos pin to the major and won't receive the change otherwise.vN
默认行为是产品决策,而非实现细节——大多数用户永远不会修改它,因此默认行为几乎就是所有用户的实际行为。选择能保留当前行为的默认值,任何有风险的功能都设为可选启用。然后发布确保现有用户不受影响的构件:
- 已发布包的行为变更→添加changeset(),以便变更被版本化并纳入变更日志。
nr changeset - JSON报告格式变更→编辑,保留现有格式或升级
core/src/schemas.ts,并运行schemaVersion。CI流水线会解析此输出,因此未提前告知的格式变更会悄无声息地破坏它们。nr smoke:json-report - 评分权重或算法变更→所有用户的分数会立即变化,基于阈值的CI门禁可能会翻转。将其视为破坏性变更:添加changeset并明确说明分数变化。
- 变更→创建
action.yml标签并移动浮动的vX.Y.Z标签,因为其他仓库依赖主版本标签,否则不会收到变更。vN
5. Update the docs
5. 更新文档
A surface nobody can discover is wasted, and a stale doc is a trust bug. Documentation is part of "done," not a follow-up:
- The / usage text next to the new flag or command, so it's discoverable from the CLI itself.
--help - The website page and the canonical prompt under , which is what agents fetch at runtime.
react.doctor/prompts/... - The distributed skills (,
skills/react-doctor) when the change alters the user-facing workflow.skills/doctor-explain
无人发现的交互面是浪费,过时的文档是信任漏洞。文档是“完成”的一部分,而非后续工作:
- 新参数或命令旁的/使用文本,确保用户能从CLI中发现它。
--help - 官网页面和下的标准提示词,这是Agent在运行时获取的内容。
react.doctor/prompts/... - 分布式技能(、
skills/react-doctor),当变更影响用户面向的工作流时。skills/doctor-explain
6. Record the kill metric
6. 记录淘汰指标
Surfaces accrete: every unused flag and dead config key is permanent maintenance and one more thing for users to misread. Decide up front how you'll know this one didn't earn its keep. Write one line in the brief — the metric from step 3, a threshold, and a horizon, e.g. "remove if stays under N after 2 releases". Schedule the check instead of trusting yourself to remember it, and treat deleting unused surface as a win, not an admission of failure.
<metric>交互面会不断累积:每个未使用的参数和废弃的配置项都是永久的维护负担,也是用户可能误解的内容。提前决定如何判断该功能是否值得保留。在简要说明中写一行内容——步骤3中的指标、阈值和时间范围,例如“如果<指标>在2个版本后仍低于N,则移除”。安排检查时间,不要依赖自己的记忆,并将删除未使用的交互面视为胜利,而非失败的承认。
7. Run checks
7. 运行检查
bash
nr typecheck && nr test && nr lint
nr smoke:json-report # only if you touched the JSON reportbash
nr typecheck && nr test && nr lint
nr smoke:json-report # 仅当修改了JSON报告时运行Brief
简要说明
Fill this in as you work the steps; fold it into the PR's "Why".
md
undefined在完成步骤时填写此内容,并将其纳入PR的“Why”部分。
md
undefinedProduct brief: <change>
产品简要说明: <变更内容>
Job: <who / goal / what they do today>
Change: <smallest surface that does it>
Reuse: <what you searched; what you extended, or why new surface was needed>
Metric: <METRIC name or wide-event attribute + the number that means success>
Compat: <default state; changeset / schemaVersion / action tag>
Kill: <metric threshold + horizon that triggers removal>
undefined任务: <用户群体 / 目标 / 当前替代方案>
变更: 完成该任务的最小交互面
复用情况: <你搜索了什么;扩展了现有方案,或为何需要新增交互面>
指标: <METRIC名称或宽事件属性 + 代表成功的数值>
兼容性: <默认状态;changeset / schemaVersion / action标签>
淘汰规则: <触发移除的指标阈值 + 时间范围>
undefinedStop and ask when
需暂停并确认的场景
These are the moments to surface a decision instead of pushing through — each one is a sign the change needs a human call:
- You can't name a job the current surface can't already do → propose reuse or dropping it rather than adding surface.
- You can't define a metric for it → either add the instrumentation or reconsider whether the change is real.
- It breaks a published contract (package behavior, JSON , the score, or action I/O) → confirm the changeset, version bump, and migration path before landing.
schemaVersion - It would grow scope beyond what was asked → get explicit agreement before expanding.
To land the change once the pass is clean, use (deslop, changeset, PR, babysit).
ship这些场景需要明确决策而非直接推进——每个场景都表明变更需要人工判断:
- 你无法指出当前交互面无法满足的任务→建议复用现有方案或放弃变更,而非新增交互面。
- 你无法为其定义指标→要么添加监控,要么重新考虑该变更是否有实际意义。
- 它破坏了已发布的契约(包行为、JSON 、评分或Action输入输出)→在合并前确认changeset、版本升级和迁移路径。
schemaVersion - 它会超出需求范围→在扩展前获得明确同意。
当本流程检查通过后,使用流程(deslop、changeset、PR、跟进)发布变更。
shipProduct principles (React Doctor)
React Doctor产品原则
Tie-breakers when a step is ambiguous — when two options both seem fine, pick the one these favor:
- Fewer findings > more findings. Ten issues a developer will actually act on beat a hundred they scroll past; cap and rank rather than dump everything.
- Root causes > individual warnings. Collapse the symptoms of one underlying problem into a single finding instead of reporting each echo separately.
- Evidence > assertions. Show the proof — the code, the measurement, the reason — rather than asking the user to take the tool's word for it.
- Actionability > correctness. A fixable issue with a clear next step beats a technically perfect finding nobody knows what to do with.
- Workflows > dashboards. Meet developers where they already work — the CLI, PRs, the editor — instead of sending them to a separate surface.
- Education > diagnostics. Explain why something is a problem so the user learns, not just that it is one.
- Trust > detection coverage. The best finding is one the user believes and acts on; one false positive costs more trust than a missed issue costs coverage.
当步骤存在歧义时的决策依据——当两个选项都可行时,选择符合以下原则的选项:
- 更少的发现结果 > 更多的发现结果。开发者实际会处理的10个问题,胜过他们会跳过的100个问题;应限制数量并排序,而非全部输出。
- 根本原因 > 单个警告。将同一潜在问题的症状合并为单个发现结果,而非分别报告每个表现。
- 证据 > 断言。展示证据——代码、测量数据、原因——而非让用户仅凭工具的判断接受结论。
- 可操作性 > 正确性。有明确下一步修复方案的问题,胜过技术上完美但用户不知如何处理的发现结果。
- 工作流 > 仪表盘。在开发者已在使用的环境中提供服务——CLI、PR、编辑器——而非引导他们到单独的界面。
- 教育 > 诊断。解释问题的原因,让用户学习,而非仅仅告知存在问题。
- 信任 > 检测覆盖率。最好的发现结果是用户相信并会采取行动的;一次误报损害的信任,胜过一次漏检损失的覆盖率。",