‘Resilient’ 是什么意思?——定义 Drift 和 Invariants
Source: Dev.to
CSS 设计中的漂移
在第 1 部分,我主张 CSS 架构应从“需要记住并遵循的规则”转向“反馈系统”。但弹性(抗漂移)设计到底意味着什么?本节将把设计退化的现象定义为漂移,并引入不变量的概念来防止它。
CSS 设计不会一次性全部崩溃。小的决策不一致会逐渐累积,等你注意到时,整体的一致性已经丧失。我把这称为 漂移。
分解
在本系列中,“分解”专指以下三件事:
- 组件边界模糊——不同人对一个组件何时结束、何时开始的判断不一致。
- 布局职责混淆——margin 属于父级还是子级在不同文件中各有说法。
- 命名与结构不一致——命名约定和文件组织在项目中各不相同。
示例:BEM 项目中的常见漂移
| 情况 | 漂移 |
|---|---|
一个团队成员在子组件本身上写 margin,另一个则在父组件上指定。 | “这个元素是独立组件还是其父组件的一部分?”——不同人给出不同答案。 |
| 修饰符的粒度在不同文件之间不统一。 | – |
这些都不是“规则违规”。指南中的解释空间导致了决策漂移,而随着团队规模的扩大,这种空间会进一步扩大。
AI 漂移
引入 AI 编码代理并不会改变结构。即使将指南全部塞进上下文窗口,输出仍会在需要判断的地方出现差异。人类的漂移就会直接转化为 AI 漂移。
约定式设计的弱点
约定式设计存在一个结构性弱点:
- 记忆负担——指南越长,所有人准确记住并应用它们就越困难。
- 主观决策——诸如“一个组件的边界在哪里?”之类的问题没有客观答案。
- 验证困难——是否违反约定无法机械判断,只能依赖审查。
当这三者同时出现时,漂移不可避免。审查只能在审查者判断保持一致时维持质量,而审查者是人——他们的判断也会漂移。
不变式
编程中有 不变式 的概念——在程序的每个时刻必须保持的条件(例如,数组索引保持在界限内,账户余额永不为负)。这些条件通过类型和断言以机械方式强制执行。
同样的思路也适用于 CSS 架构:消除答案会因解释而改变的规则,只采用能够机械评估为真或假的条件。
具体示例
| 基于约定(需要解释) | 不变式(机械可验证) |
|---|---|
| “将组件分解为适当的粒度。” | “块(Block)的直接子元素只能是块(Block)或元素(Element)。” (terms defined in Part 4) |
| “父级管理布局。” | margin-top只能在父级的 > .child 选择器中指定。 |
| “保持命名一致。” | 块使用两词 kebab‑case 命名;元素使用单词命名。 |
| “区分状态和变体。” | 变体使用 data-variant;状态使用 data-state。 |
左列需要人工解释来判断正确性。右列可以通过字符串模式和选择器结构匹配来判断,从而实现自动化 lint 验证。右侧示例是真实用于 SpiraCSS 的不变式,我在生产环境中开发并使用。第 4 部分对它们作了详细阐述。
可行不变式的属性
并非所有规则都能成为不变式。要作为不变式使用,规则需要具备以下特性:
- 二元评估 – 违规或合规的判断必须明确无歧义(没有灰色地带)。
- 语法可验证 – 只需通过源代码的语法分析即可确定(无需运行时)。
- 局部可验证 – 只需查看目标文件本身即可确定(不需要全项目扫描)。
如果设计完全由满足这些标准的规则构成,Lint 工具就可以充当“守门人”。人类无需做出判断——只需遵循工具的裁定。AI 代理也是同理。
处理异议
“这不是只是在冻结偏好,而不是定义‘正确性’吗?”
这是一个合理的异议。 “从父元素写入边距” 并不是唯一正确的方法,但固定设计偏好以使所有作者在同一输出上达成一致是有价值的——尤其是当作者群体包括人类和 AI 时。拥有一个坚定不移的标准本身就能提升团队生产力。
反馈循环
仅仅定义不变量并不能保持设计。若不检测违规并传达问题所在以及如何修复,纠正就不会发生。其顺序:
- 定义不变量。
- 检测违规。
- 提供修复指示。
构成一个反馈循环。下一部分将更深入地设计此循环。当 lint 不仅返回“有什么问题”,还提供“如何修复”时,错误信息必须精心编写。
下一步
→ 第 3 部分 — 反馈循环 — 设计能够告诉你该修复什么以及如何修复的 Lint(本周稍后)
资源
- SpiraCSS – 设计规范、工具和源代码均为开源。
- 网站:
- GitHub: