跳转到内容
Yuuki学习手记
GitHub

简单、组合、复杂性:用什么问题阅读一个造物

本轮的研究假设是:好的产品让少数稳定概念覆盖很多真实任务;好的实现让常见变化只影响少数责任边界。 这两件事可以互相帮助,也可能互相冲突。界面只有一个输入框,后台仍可能是一套庞大的执行系统。

这是一套针对本轮案例的分析框架,不是复杂度定理、产品评分或选型公式。相关:调查入口 · Raycast / Warp · Zed / Grok Build · Yuuki 的变化实验。

1. 先说明:谁觉得简单,做什么时简单

Section titled “1. 先说明:谁觉得简单,做什么时简单”

Rich Hickey 在 Simple Made Easy 中区分了结构上的简单与使用者感到容易:前者关心责任是否纠缠,后者受熟悉程度和可获得性影响。这里借用这个区别,不把某种语言、范式或所有可变状态一概判为复杂。演讲原始资料,2011

本轮把“简单”拆成四个观察面:

观察面具体问题容易混淆的现象
用户操作找到目标、判断状态、完成动作,要记住几套规则?按钮少,却必须自己搬运上下文、猜进度
局部实现修改一项行为,必须同时理解哪些状态与责任?文件短,却依赖大量隐含调用顺序
组合使用一个能力接入另一个入口,需要重复多少业务规则?都能调用,却无法传递结果、等待完成、取消
维护与运行出错后能否定位原因、解释副作用、恢复工作?演示流畅,异常后只剩下重启和猜测

这四面没有天然同一的最优点。Raycast 的扩展作者容易开发,可能来自宿主承担运行时与桥接工作;Grok Web 用户容易发布,可能来自平台接管部署与授权。它们的价值是真实的,但应把成本的承担者写出来。Raycast 案例 · Grok 产品边界

少代码、少语言、少进程、少依赖都有具体收益,但都只是线索。 一个进程减少通信故障,也共享崩溃与资源边界;两个进程增加生命周期工作,也可能复用成熟生态。上轮 PI 调查已经展示了 Electron、Node Agent 与 Rust host 的这类交换;仅数 crate 无法回答整体是否更好理解。PI 的责任与协议

Parnas 的模块划分讨论强调:模块是责任分配,可以围绕困难或容易变化的设计决定来划分,让其他模块不必知道这些决定。他用不同分解方式比较改动传播,而非仅按处理流程逐步分文件。本轮将“变化会扩散到哪里”作为主要检验方法。Parnas 原文,1972,大学转载

下面是本轮自拟的组合检查表。它不要求每个纯函数都具备持久化协议;涉及异步、有状态或跨进程能力时,再补齐相应项目。

接缝必须讲清的事研究中的反例
对象与身份两端操作的是同一个什么?ID 的寿命有多长?屏幕第 3 行不能充当跨更新的任务身份
输入与结果参数怎样获得?结果是值、句柄还是通知?同为 JSON,不代表字段语义一致
状态权威谁能决定运行、取消、失败和完成?UI 和 Runner 分别猜一份最终状态
时间与顺序接受、开始、输出、完成分别如何表示?Raycast 启动确认不能代替业务完成
生命周期关闭入口、丢弃句柄、断开连接会发生什么?Whisper 的 stop 与 cancel 不能合并
失败与副作用超时后结果未知还是明确未发生?能否重试?写入已发生,取消并不使它消失
资源与观察谁限流、截断、清理?如何查有效配置?丢弃积压快照仍可能付出完整序列化成本

反例的源码和 API 依据分别见命令与 Block、Grok 的扩展与会话、本地三条变化链。

还需要分开四种常被混称的组合:

  1. 呈现组合:把列表、详情、按钮放进同一界面,解决布局与交互。
  2. 能力组合:不同入口调用同一业务操作,复用参数校验、执行和结果。
  3. 执行组合:让任务可靠衔接,处理等待、取消、失败与已发生的副作用。
  4. 部署组合:通过进程或网络协议分工,处理版本、连接、身份与资源寿命。

Warp 的丰富内容共用滚动区域,首先证明呈现组合;Raycast Form 与 AI tool 共用函数是能力组合的设计方式;PI 的 turn 与持久化涉及执行组合;Grok ACP 则提供进程协议入口。某一层成立,不能自动证明下一层成立。

3. 复杂性既可以搬动,也可以实际减少

Section titled “3. 复杂性既可以搬动,也可以实际减少”

Brooks 在 No Silver Bullet 中区分问题本身的概念难度与表达、工具带来的附加成本,并讨论外部约束带来的复杂性。这里用它提醒自己区分成本来源;不把历史论述变成对今天 AI 或工具进步的绝对预测。原始技术报告

本轮采用以下工作分类,分类可以随需求变化,不宣称某个问题永远不可简化:

来源例子可以怎样处理
已接受需求带来的状态流式输出、审批、取消与文件写入竞态明确状态和责任;也可缩小需求减少组合
平台与兼容约束输入法、PTY、Shell hooks、音频格式、子进程退出集中到适配层,给调用方保留必要失败语义
自己引入的纠缠多份权威状态、重复校验、不必要中转、共享全局开关合并责任或删除机制,真正减少总工作
尚未需要的通用性任意插件、全套云编排、通用原生控件协议延后到具体复用压力出现

因此,不采用“复杂性总量守恒”的说法。删除需求会减少状态空间;移除重复权威会减少同步关系;限制一次只运行一个任务会避免一批并发交互。另一方面,若只是把部署交给平台,复杂性主要改变了承担者,仍会以费用、可观察性、迁移和故障处理的形式影响使用者。

“隐藏”也有好坏之分。调用方不必知道 Whisper 如何转换音频,但需要知道停止录音后还在转写;调用方不必知道数据库布局,但需要知道收到的是接受确认还是持久化完成。好的边界隐藏实现决定,保留影响下一步行动的事实。 这是本轮从案例中归纳的判断,并非引用某产品的设计宣言。

自由度越高,不意味着用户越容易表达意图。如果每个扩展都重新定义选择、确认、返回和快捷键,用户就要为每个能力重新学习一套操作。Raycast 固定条目和动作的交互语法,把变化留给业务内容;Zed 让单文件也经过 MultiBuffer,使普通编辑和片段编辑共享后续能力。动作契约 · MultiBuffer 证据

这里的启发是:优先寻找能被多项功能共同使用、语义又足够明确的单位。这个单位不一定是最小函数,也不一定要抽成独立包。它可能是一次执行、一个可编辑片段、一项业务操作或一个会话。

也要留意反例:为了复用,把所有对象压成一个任意 JSON 结构,会把检查推迟到运行时;为了统一,把停止录音和放弃转写变成同一按钮,会损失必要意图;为了通用,把不同生命周期强塞进同一抽象,会使每个调用方都增加例外分支。

先固定一个真实变化,再记录以下内容,得到可复查的比较:

  • 为了理解它,实际读了哪些模块、协议和状态;不要只统计最终修改文件数。
  • 真正修改了哪些责任边界;是否重复修改同一条规则。
  • 新增了哪些状态、跨边界消息和顺序假设。
  • 错误、取消、重复请求、旧结果晚到时,用户会看到什么。
  • 运行行为是否验证;哪些只是静态阅读推断。

这些是观察项,不能相加成一个客观“简单分”。同样也不能把 Zed、Raycast、Warp、Grok 的规模和产品任务当作等价负载比较。对 Yuuki 更有价值的,是在五条 UI 路线上对同一个业务变化留下证据;具体探针见 04 · Yuuki。

本轮对“璀璨造物”的解释由此落地:它们值得欣赏的地方,是把人的意图、可操作对象和反馈组织得连贯。研究实现时,则继续追问这种连贯依赖哪些契约、付出了什么成本、在哪里需要承认边界。