创刊号刊出了《无需安装的应用的时代》。它画的是:一处十分钟的修改,要走到用户画面上会经历什么 — 构建、审核、分发,以及等着用户去按更新 — 那整条锁链。
下一期刊出了《我们为什么放弃了编译》。它把那条锁链的第一环当作一种物性重读了一遍。编译,就是把那一刻的所有决定冻成一整块硬邦邦的东西这句话,是这本杂志六个月里写出的最好的概念压缩。
两篇是同一道山脊的正反面。一篇问锁链消失后会变什么,另一篇问它当初为什么会在。所以谱系、三种绕道尝试、反驳问答,都各立了两遍。这次合成一篇。
而合并时还有一处要改。两篇都在没有数字的情况下谈数字。
「画面定义很小。不是沉重的代码包,而是『一个按钮、一段文字』这样轻的规格。」
写了「小」,却没量过是多少字节。写了「一边三天,一边十秒」,却没给那十秒计时。当时没有可量的东西。现在有了。
冰 — 编译做的事
先说要留的。把编译读作物性的那一段原样保留。
做一个应用,就是把那一刻的所有决定冻成一整块。按钮在哪、什么颜色、按下去会发生什么 — 这些决定经过编译在设备里凝固。凝固之后一个字符都改不了。要改就得再冻一次、再过一次审核、再分发一次,然后等用户愿意收下。
分发之痛不是因为懒惰或工具老旧。那是「画面」这个决定被冻在设备里这一结构的必然。
这里立起一个问题 — 原文所定式化的那个问题。能力和冰真的分不开吗? 要用原生能力就必须冻起来,还是可以只化掉画面这一层、把能力那一层继续冻着?
我们花六个月真的做了一遍。先写答案。能分开。而且必须继续冻着的东西比想象中多。
定义到底有多小 — 实测
先偿还原文最弱的那一句。「定义很小」没有数字。
现在可以真的量了。以下全部是从跑起来的东西上取到的大小。
| 什么 | 大小 |
|---|---|
STM32H723 开发板递来的 ui://app(一个页面) | 656 B |
ESP32 递来的 ui://app(带路由的应用) | 158 B |
该应用的第一个画面 ui://page/main | 1,894 B |
| 门店服务器的自助机画面 | 1,241 B |
| 同一服务器的 POS 画面 | 1,170 B |
| 同一服务器的后厨画面 | 1,144 B |
| 无人店铺整个包(4 个 JSON) | 约 6 KB |
一个画面是 1〜2 KB。「小」这个说法是对的。只是当时并不知道它对不对就写了,现在知道了。
有了数字,原文说不出来的一件事就挂上了。门店服务器分别递出三个画面,三者合计 3,555 字节。若按旧办法,这里本该做平板应用、POS 应用、后厨应用三个,各自过审、各自分发。不是三个应用被 3.5 KB 取代了,而是三个应用存在的理由没有了。
(测量出处:接到一块芯片上,接上的设备就成了终端 · 开发板递出自己的画面 · 一个文件夹就是应用)
十分钟的修改要多久 — 实测
原文所画锁链的另一端。不冻起来的话,真的立刻抵达吗?
我们改了无人店铺应用的画面 JSON 再跑一次。日志如下。
[+ 1415ms] edited ui/pages/main.json — 2940 B -> 2934 B, no compiler ran
[+ 1419ms] route "/" -> ui://pages/main ("Unmanned Store")
[+ 1498ms] captured 04_edited_json.png
从编辑到画面反映是 83 毫秒,编译器没有运行。 路由跳转是 4 毫秒。因为画面切换就是查一次文件。
原本写着「十秒」的地方,现在填进了真实数字。而这里也暴露出原文没看见的一件事 —83 毫秒不是因为没有构建,而是因为没有分发。 就算构建缩到 0.1 秒,只要审核、分发和用户点更新还在,锁链就原封不动。化掉的不是编译,而是整条锁链。
什么至今仍必须冻着
这是两篇里《放弃了编译》最好的一节,而且真做出来之后证明那一节是对的。只是原文也把它写成了叙述。
实时循环保持冻结。 在 LCD 面板样例里,温控循环在 C 固件内部。
/* First-order thermal: dT/dt = k·(target − T). Converges to target
* with a little sensor noise. */
static void thermal_update(void) {
/* ... */
double alpha = 1.0 - exp(-k * dt);
g_temp += (g_target - g_temp) * alpha;
}
这东西下不到画面定义里来。以毫秒为节拍转动的东西,不能待在网络的另一头。
安全范围也保持冻结。 而且这一条更硬 — 不管上面让它做什么,硬件都不会越过自己。
/* The relay clamps its own range. Whatever the rule upstream
* decided, the hardware still refuses to exceed itself. */
if (v < 0) v = 0;
if (v > 100) v = 100;
原文写的是「实时、驱动、安全保持冻结,只有画面层变成液体」。做出来之后发现那份清单还得加一行。自己守住自己极限这件事。 规则是液体,就意味着规则可能是错的;那时最后拉住的那只手,必须在冻着的那一侧。
支付授权路径也保持冻结。 刷卡终端的安全区域不打开。不是打不开,而是不打开更好,理由另写在不拿出来的东西守住了应用。
(固件边界见LCD 厂商 30 分钟做出 HMI,继电器钳位见换掉传感器,控制照旧)
协议的名字 — 两篇一路都没喊出来的
两篇都把这个协议一路留作无名。「约定好的语言」「开放的共通语言」。一篇主张「不开放就不成立」的文章,如果把协议所指模糊掉,读者就无处可去核实。
它的名字是 MCP(Model Context Protocol)。而且这个系列的每一个样例都说它。真实的字节长这样。
=> {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18", ...}}
<= {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26",
"capabilities":{"tools":{},"resources":{}},
"serverInfo":{"name":"WeAct H723 MCP Node","version":"0.1.0"}}}
这是真实的 STM32 开发板通过 USB 串口回的一行。而「它是开放的」这个主张现在也有了依据 —同一份客户端代码接上了 STM32(UART)和 ESP32(Wi-Fi TCP)。 两块板互不知晓,是不同公司的芯片,传输也不同。它们仍然通了,因为说的是同一个协议。
原文写的那句「和浏览器能画出任何一家公司服务器的 HTML 是完全同一个原理」,如今不再是比喻,而是两块板的日志。
再答一次那些反驳 — 这次带数字
两篇各立了五组问答,其中四组重合。只立一次,能答得上的就改过来答。
「每个画面都从服务器取,不会慢吗?」— 画面定义是 1〜2 KB(见上表)。而往返取决于链路。在某一次运行里,UART 侧的工具往返是 1〜11 ms,Wi-Fi TCP 侧是 21〜69 ms,从连接到第一个画面是 46 ms 对 189 ms。所以准确的答案是这样 —不是「不慢」,而是链路不同、量级不同,而这个差别会分开 UI 的设计。 个位数那一侧可以指望「一按就有」;几十毫秒那一侧就必须把等待状态画到画面上。原文不知道有这个分叉。
这里补一句。时间值不可复现。 大小是文件,量多少次都一样;而延迟每次运行都在晃,某一次运行里 Wi-Fi 那侧超过了上述数值的两倍。所以这篇文章只主张量级,不主张精确的毫秒。
「离线呢?」— 这条还还不上。原文写着「定义会被缓存」,而这个系列的任何样例都没有试过缓存与离线行为。 重连行为也没量过。作为未验证保留。
「安全呢?」— 原文的答案 — 运行时不信任定义、只允许固定的组合 — 依然成立。在此加上做出来之后学到的。边界不是一道而是三道(接收侧的运行时、暴露侧的设计、守住自己极限的硬件),而不在工具清单里的能力,人去按也好模型去选也好都够不着。实证在另一篇里。但「所以它是安全的」这个系列没有证明 — 认证、权限、传输安全全在外面。
「应用商店政策呢?」— 原文的答案原样保留(界线在于是否植入任意原生代码)。原文自己加的但书也原样保留 —这是政策解读,不是法律意见。 这里再加一条:两篇都没有引用哪怕一行依据条款。没有引用的解读不是解读而是猜测,所以这一项在补上引用之前标记为未解决。
「服务器成本呢?」— 保留原答案。只是这个系列做的服务器全是本地进程,我们从来不在能测量运营成本的位置上。
这个样例没有做的事
原来两篇的诚实一节保留。编译不会消失,只是换了地方;既有系统不会一夜变身;叫做设计的那份劳动依然存在。做出来之后,三条都成立。
在此加上这次学到的。
两篇的思想实验就是思想实验。 门店设备的场景也好、创刊号的场景也好,都是以「假设」开头的想象,却因为写得具体而读起来像观测。这篇文章引用的全部是运行日志和实测,并逐条用链接标明来自哪一篇。我们不把想象和观测排成同一种字体。
测量很窄。 上面的数字来自一台 Mac、一台路由器、一个房间,多数只量了一次。样本少、环境单一。大小是确定的(文件就那么大),而延迟每次运行都会变 — 就在写这篇的过程中,先前引用的延迟数值因为重跑而变了,我们为了与刊出的日志对上又改了一次。引用时间的文章,会和它的日志一起变老。
三件事仍未偿还。 离线与缓存行为、应用商店政策的依据引用,以及运营成本。原文给过答案的地方,至今没有实测。
所以,再怀疑一次
原文就是这样收尾的。去怀疑,接下来的文章会拿实物给你看。
六个月过去了,那些文章出来了。这篇引用的数字全部来自它们,每一篇都附着构建日志、运行日志和无头拍下的画面。想复现,把文件夹拿走跑一遍就行。
而我们仍然留着可以怀疑的地方。离线没量过,政策没挂依据,成本我们不在能测的位置上。 那三件事还停在当初的位置。
画面这一层化了。这件事现在可以用 656 字节和 83 毫秒来说。什么必须冻着也知道了,而那份清单比一开始想的要长。剩下的是还没量的,那些等量过之后再写。
makemind.dev 「探索」— 当时没测就写下的数字已换成实测。