简单设计如何胜过堆代码
1638 字
4 分钟
简单设计如何胜过堆代码

差点给 Hyprland 打补丁#

起因是一个很小的需求:我希望我可以快速知道 Hyprland 里面有没有窗口还打开着。

很朴素的想法,对吧?

第一反应:加一套 API#

先前我为了让 Alt+Tab 弹出一个窗口切换面板,写过一个小工具 WindowSwitcher——一个 2.6 KB 的 Python 脚本。就这么点东西,它拖着的配套却是:

  • 一个独立的 Forgejo 仓库;
  • 一条 .forgejo/workflows/package.yml 打包流水线;
  • 一个 registry 包(window-switcher-1.0.tar.zst);
  • 一条 ansible 的 vendored 组件登记,含 sentinel 校验路径;
  • MANIFEST.md 里一整节说明文档与 README 里的清单条目;
  • 以及 hyprland.lua 里的一条快捷键绑定。

当时的思路是这样的:Hyprland 没有直接对外暴露「每个工作区里都有哪些窗口、当前聚焦的是哪个」这样一个接口,那我就在合成器里加一个。改 C++,把窗口树按工作区聚合,序列化成 JSON,再让 Waybar 去读。

这条路听起来很「彻底」,但它意味着:

  • 维护一个 Hyprland 的私有分支(当时已经是 vedaru/v0.56.2-ime-popup,还要再叠一层);
  • 每次上游发版都要处理 rebase 冲突;
  • 引入一个新的数据契约,两端一起演进;
  • 而这一切,只为了在一条 24 像素高的状态栏上加几个字。

代码是能写出来的。问题在于,它值不值。

先问系统有没有这个原语#

真正把整件事推翻的,不是更聪明的架构,而是先停下来读了一遍 Waybar 的手册。

事实是:Waybar 的 hyprland/workspaces 模块早就为每个工作区按钮加好了 CSS 类——.active、.visible、.urgent,以及一个几乎没人注意的 .empty:当一个工作区没有窗口时,它会被自动打上这个类。

也就是说,「哪个工作区里有窗口」这个信息一直在那儿,只是从来没有被使用过。我不需要新 API,不需要改合成器,甚至不需要碰 Waybar 的配置文件。我要的全部东西是:

/* 有窗口的工作区:在数字背后垫一块更亮的色阶 */
#workspaces button:not(.empty) {
background-color: @surface-container-highest;
}
/* 当前聚焦的工作区:再上一层 */
#workspaces button.active,
#workspaces button.focused {
background-color: @primary-container;
color: @on-primary-container;
}

三行。没有分支,没有数据契约,没有需要跟进的接口。上游怎么改,这段 CSS 都不需要动。

这里还有一个我中途才想明白的细节:一开始我把「有窗口」实现成了改变数字的颜色,但那是错的。占用状态应该由按钮的表面(背景色块)来表达,数字本身保持安静——这正是 GNOME 46+ 的强调色体系和 Material 3 在做的事:层级由明度台阶区分,而不是靠给文字上色。设计对了,代码反而更短。

等到这条需求本身被更好的方案取代(工作区色块已经把「哪里有事」说清楚了),我把整个自研的工具退役掉,hyprctl -j binds 里 ALT+Tab 的绑定归零。删掉的东西包括那个二进制、那段选择器函数、版本表里的一行、comps 数组里的一个名字、文档里的一节——六处配套,为了一个 2.6 KB 的脚本。

这就是复杂度的真相:它从来不是按「代码行数」计费的。一件自研工具的真实成本,是它牵出的仓库、CI、包、分发、文档和心智负担的总和。写的时候只看到那 2.6 KB,维护的时候要面对的是那六处。

为什么「加」总是先于「减」#

回头看,第一反应之所以是改合成器而不是读手册,有一个很现实的原因:写代码是有即时反馈的,而做减法没有。

加一个 API,你能看到编译通过、数据流动、界面亮起来,每一步都在给你正反馈。而「先读文档、发现人家已经做了」,它没有产出物,只有一次判断——甚至可以说,它看起来像什么都没做。于是我们本能地滑向「我要造一个」,因为造东西的手感是具体的。

但恰恰是这种手感,最容易掩盖一个事实:能被删掉的那部分,往往才是真正的解法。

几条可以带走的判断#

  1. 先找原语,再造轮子。 在动手加一层之前,先确认系统是否已经暴露了你需要的最小信号——它可能只是一个你没用过的 CSS 类、一个已有的环境变量、一个现成的 hook。绝大多数时候,你以为要「扩展系统」,其实只是没把系统读完。

  2. 状态用表面表达,而不是改写内容。 「有窗口」是按钮的状态,不是数字的属性。把它放在背景色上,语义就落在正确的层级,样式规则也随之变短、变稳。

  3. 按总成本给「小工具」定价。 一个脚本的重量,等于它带来的那条流水线。在写之前先问:它会不会长出自己的仓库、CI 和文档?如果会,它就不是小工具。

  4. 删除要趁早,且要删干净。 只删二进制而留着 registry 登记,下次部署会把它重新拉回来;只删绑定而留着文档,下一个人会以为它还在用。整套退役,比留下半截更便宜。

结语#

那次返工之后,Waybar 上什么都没有变多——合成器没被动过,分支没变长,接口没增加,代码行数反而少了一点。但工作区区块的明暗,已经把该说的话说完了。

我们总以为「更强的系统」意味着更多代码。可在这个案例里,更强的设计意味着更少的代码:不用补丁,不用 API,甚至不用那个存在了三周的自研工具。

最好的工程决策,有时是一次成功的删除。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

简单设计如何胜过堆代码
https://www.vedaru.cn/posts/simple-design-over-code/
作者
Vedaru
发布于
2026-09-26
许可协议
CC BY-NC-SA 4.0
加载评论中…
距离上次编辑: 1 天 12 小时 55 分 42 秒

部分信息可能已经过时

银色飞行船
Sawako碎花
银色飞行船
Sawako碎花
0:00 / 0:00