Fragments

三年前刚接触存在主义的时候,我觉得萨特简直是再生父母——一位严厉的慈父。因为他让我们每个人认识到人的自由与必须自由的责任。

现在我觉得他他妈的也能算哲学家?他凭什么能把「羞耻」这种主观情绪直接强行等同于「他者存在」的本体论绝对证明?纯粹他妈脑子有病。

而且他做哲学的方式跟神学和玄学几乎没区别,毫无认识论上的谦逊。虽然作为哲学家必然会受限于自己的时代,容易基于自身经验给出根本不可能是永恒真理的断言。这是因为作为认识论的基础——人类的主观生活经验必然会受限于时代,哲学家也很难避免这个问题。但这种狂热与傲慢的人类中心主义思想还是让我觉得 sick。

NotebookLLM 和 Claude Opus 指出萨特不是不知道神经科学和心理学,而是刻意且明确地拒绝它们作为本体论基础。他在书里直接讨论了神经元、解剖学、活体解剖,甚至想象了「能看到自己眼睛」的设备。所以「时代局限」甚至不是主要问题,方法论上的刻意封闭才是。

与之相比,康德的「物自体」概念,及其背后对认识论边界保持谦逊的态度,让我觉得在这一点上康德比萨特高明多了。

萨特拒绝物自体,让他的形而上学承诺消除了「社会有不可改造的深层结构」这一可能性。在这种形而上学下,任何否定深层结构的乌托邦工程都变得原则上可行;在康德或复杂系统论的形而上学下,这种宏大工程从一开始就有结构性的理由值得怀疑。这种形而上学的狂妄必然有它的代价。萨特后期在一些具体历史判断上留下的污点,就是这份代价的具体显形。


从哲学意义上,一个人必须完成弑父才能真正获得自由。我今天所做的正是对萨特的弑父行为。但我杀死的不是他作为人或作为我哲学父亲身份的存在,而是他思想中狂妄自大拒绝承认不可知性,试图成为全知全能的神的极端人类中心主义部分。

笑死,自己做基于 WKWebView 的浏览器后 Safari 和 Brave 的弱智问题都自动消失了:Safari 在 Gemini 里输入完中文要按两次 Enter 才能发出去消息;Brave 同时按 l b 快捷键点赞书签 tweet 会随机失效一个,太弱智了。

我觉得 Brave 就是他妈功能做太多了,经常都在更新但更新没体验到任何好处,反而莫名其妙不断引入此前从来没有过的小问题。

「Sans 字体根本不是用来阅读严肃创作的!」花了一晚上时间,终于调试好了中文字体选择。之前使用 PingFang 是因为我喜欢的 STSongti 对加粗的区分非常不明显,尤其是在 Dark Mode 下几乎看不出来加粗效果。尝试了多种选择,最终解决方案是 PT Serif -> STSongti -> Noto Serif (Web Fonts) 的 fallback 链,中文加粗和标题统一使用 600 字重,加粗额外使用下划线强调并在 Dark Mode 下提高文字亮度。

虽然 Noto Serif 设计更现代,但我个人还是更喜欢 STSongti,因为它的字形更紧凑偏瘦,气质更好。字重 600 是因为 700 下 Noto Serif 像疯了一样在标题里突然笔画粗细对比强烈,看起来非常难受。现在应该全平台视觉效果都非常接近了,Apple 平台可以获得原始体验。

一个小彩蛋,如果实在不喜欢 STSongti,可以点击链接强制以后都使用 Noto Serif。或者恢复默认字体

为了给 Veil 写发布文章,发现 Astro 没法正确渲染一个带反引号的快捷键: `Ctrl+\``。让 Claude Code 研究了半天才知道是它自己写错了,CommonMark spec 的规范写法是 `` Ctrl+` ``,这样写才能正确渲染出 Ctrl+`。然后发现 Neovim 里渲染也不对,没有如 CommonMark spec 规定渲染时去掉 `` `` 内部的两个 padding 空格。再让 Claude Code 研究,最后给 Neovim 插件 render-markdown.nvim 提了个修复的 PR。一个发布公告硬生生变成了整条工具链的 debug 日志。

BTW, 这条 fragment 的源代码长这样:

这条 fragment 的源代码

终于让 Claude Code 拿当下最热门的静态网站生成框架 Astro 帮我把网站弄起来了,结果发现一个荒谬的问题:即便在生产环境上,页面加载完成之后点击任意站内链接,也会触发一个字体闪烁效果。按理说这完全不应该——第一次页面加载完字体都下载好被浏览器缓存起来了,为什么开新页面还要重新加载一次导致闪烁?

Claude Code 一开始信誓旦旦说这是正常的,我坚持这一定是框架层面的问题,要求它深入研究。结果还真是。Astro 的 Fonts API 把 @font-face 声明内联到了每个页面的 <style> 标签里,MPA 每次导航都是全新 HTML,浏览器每次都要重新解析字体声明。即使字体文件已经在缓存里了,这个「发现→匹配→应用」的过程依然会触发闪烁。

这么做的原因据说是为了消除 render-blocking 资源,让 Lighthouse 评分更好看。一个以内容网站为主要场景的前端框架,为了跑分牺牲了最基础的字体渲染体验,真是非常令人惊叹的设计取舍 — designed for benchmarks and self-praise, not for users.

修复方式反而很传统,把 @font-face 搬到独立的外部 CSS 文件里自己管理,让浏览器能正常利用缓存。Web 2.0 启蒙过去 20 年了,最可靠的方案依然是最朴素的那个。

Conventional Commits 是一个失败的设计。

显然它的意图是给纯文本 commit message title 添加结构化的 metadata 方便程序处理,例如自动生成结构化的 changelog。但要达到这个目的完全可以有不妥协人类阅读体验的做法,例如在 commit message body 里添加专门的 metadata 字段。

Conventional Commits 选择在最重要的 title 里添加 metadata 字段,增加了绝大多数时候毫无意义的视觉噪音;而且强制 title 小写字母开头,极大破坏了人类阅读 commit message 的舒适度。

任何为方便程序/机器/AI 处理数据的设计,都不应该以牺牲人类的体验为代价。

软件行业二十多年的模块化之梦已被 LLM 变相实现

昨天让 Claude Code 全自动写了一个 FC/NES 模拟器。讨论架构花了 1 小时,然后它自主执行 19 个任务,除了权限确认之外零人工干预,1 小时后马里奥和魂斗罗都能跑了。

它之所以能做到,是因为这些代码已经被人类写过很多遍。表面上看是 AI 学会了怎么写模拟器,但本质上是它替人类永久保存了这些知识,并且可以代替人类全自动复现。

二十多年前软件行业有一个梦想:所有基础功能都会被实现为可复用的模块,做新软件时只需要像搭积木一样组合。这个梦想随着开源文化的发展实现了一小部分,但远没有达到当初设想的程度。

真正的难点不是没人写模块,而是模块之间的连接成本——接口适配、版本兼容、上下文特化——往往超过模块本身。所以「搭积木」从来没有真正轻松过。

而今天 LLM 以一种极其「混沌」的方式变相实现了我们二十年前的梦。但它并没有实现代码级别的精确模块化,而是通过学习这些代码的 pattern 后重新生成来获得等效的结果。当然它也会正常地复用开源组件,但那些没有现成模块的部分,它靠模式复现来填补。

从逻辑上看,学习 pattern 再重新生成是严重的绕远路——相当于为了复用一个函数,先读遍了人类写过的所有代码,然后凭记忆重写一个功能等价的版本。这个额外成本是荒谬的。

所以这是一种「混沌」的做法,而不是当年设想的「纯粹」的模块化。但出乎意料的是,这种混沌的做法效果非常好,非常实用,甚至做到了纯粹的做法在现实中根本无法实现的效果——因为它能理解真实需求的上下文,以精确度极高的方式在正确的上下文里复现那些被「保存」下来的人类知识。

换句话说,LLM 实现的不是当年「软件模块化」的梦想,而是「已解决问题的极低成本复现」。这对绝大多数软件开发场景已经是革命性的。但那些从未被解决过的原创设计,或许仍然需要人类来思考。