← 返回博客2026-07-23

可以借鉴布局,不能继承词汇

一个临时牌桌代号渗进了目录、组件、测试、文档、机器数据与官网。真正的清理不是全局替换,而是一次有目标 schema、有迁移顺序、有语义保真和 CI 约束的品牌语言迁移。

借鉴布局时,最容易顺手带走的东西

布局可以用来参考,对方的词汇不该跟着进入产品。一个视觉参考可以告诉你底池、座位、操作区放在哪里;它的品牌名、内部代号和信息架构,并不会因为你参考了那张网格就自动属于你。

我们是在一个新版牌桌的临时代号——它直接取自我们参考布局的那款产品——逃出原型以后,真正看清这条界线的。它一路钻进组件目录、class 名、测试、截图、设计说明、机器数据,最后甚至出现在公司官网上。运行时没有坏,恰恰因此它活了下来:软件可以正常编译,产品却在不知不觉中说着不属于自己的词汇。

最诱人的修法是全仓一键替换。但那不是迁移方案。同一种写法至少可能扮演四种角色:源码标识符、面向用户的文案、文件名,或者压缩数据里一次纯随机的字节碰撞。四者一刀切,要么漏,要么把文件改坏。

真正能复用的认识是:品牌语言是一套 schema。先给它定义规范值,再按消费方类型逐一迁移;切换过程中保持行为不变;最后加一道约束,让旧值一旦回来,构建立刻变红。

动文件之前,先写出目标 schema

好的迁移从「以后叫什么」开始,而不是从「讨厌哪个词」开始。我们先写了一份很小的词汇契约,按受众给每个词分配位置:

场景规范名称原因
面向用户的文案「新版牌桌」描述体验,不暴露实现谱系。
代码、路由与测试Table2 / table2稳定、自有、容易 grep。
文章集合「博客」准确描述今天已有的东西:文章列表。
未来的编辑产品「洞察」等它有独立承诺和结构时再启用。

Table2 故意很普通。它是一个实现定位符,不是产品口号。用户不该先读懂我们的组件家谱,才能读懂牌桌。公开表达可以随着产品演进;内部定位符只需要清楚、稳定,而且属于我们自己。

「博客」和「洞察」的区别也是同一条原则。导航名称是贴在用户即将打开的对象上的标签,不是给未来愿景打的广告。把普通文章列表叫成「洞察」,不会让内容更有价值,只会让信息架构更不诚实。真正的洞察功能以后可以单独出现——带着自己的内容格式、编辑承诺和路由。

要迁移的是一张图,不是一个字符串

代号 目录 组件 (import) 路由 端到端 测试 截图/ 证据 生成数据 (base64/integrity) 公开 changelog/ 官网 pre-push + CI 守卫 1 结构 2 按受众改文案 3 机器数据

一个字符串,多个消费方:迁移按顺序切开依赖图,最后由守卫长期把关。

一个代号会沿着关系扩散:目录被组件 import,组件被路由挂载,路由被端到端测试打开,测试又产出一张以路由命名的截图。真正的工作单元是这张依赖图,不是最初那几个字符。

我们的迁移顺序是:

  1. 先冻结词汇契约。公开叫什么、内部叫什么、大小写如何、哪些是真正合理的豁免,先定清楚。
  2. 同时盘点文件名和内容。git ls-files --cached --others --exclude-standard -z 枚举已跟踪文件和未被忽略的未跟踪文件。一个 commit 干净,不代表下一份等着加入的生成文件也干净。
  3. 先改结构,再改文案。移动目录和文件,然后修 import、符号、路由 helper、selector、测试、fixture 与证据文件名,直到编译器和聚焦测试都认得新图。
  4. 按受众改写文案。用户看到的是朴素产品语言,代码拿到的是规范标识符。全局替换做不出这种判断。
  5. 机器数据单独处理。只规范那些可以证明「解码后含义不变」的表示。
  6. 再跑一次全量盘点,然后把它变成闸门。最后那次搜索不再是一张一次性回执,而是可执行的长期政策。

顺序很重要。先改文案,后续移动目录又会制造一轮 import 和 snapshot 噪音;一上来就把守卫锁死,它又可能拦住迁移必经的中间态。数据库迁移有切换点,词汇迁移也一样。

最危险的边缘:机器数据和二进制巧合

禁用写法出现在正文里,它是语言;同一串字节出现在 integrity 字符串、压缩 payload 或图片流里,可能只是巧合。政策仍然可以要求仓库原始字节干净,但修复时必须保住消费方最终解出来的值。

这就排除了无脑替换。改 base64 图片里的三个字符,图片也跟着变;改 integrity 值,依赖校验可能直接坏掉。我们改用只对特定上下文成立的等价编码,二进制则走一条 fail-closed 的人工路径:

  • 在受支持的 JSON 机器字段里,用 Unicode escape 改变源码字节,而 JSON.parse 得到的字符串完全相同。
  • 在 HTML 的 srchref data URI 里,用数字字符引用改变序列化 HTML,而解码后的属性值保持一致。
  • 在 PNG、JPEG 或 PDF 数据里,守卫只负责报告 offset 并失败;资产需要重新导出,再人工核对渲染结果——与 JSON、HTML 两条路径不同,这一步没有自动化等价测试兜底。

这个 normalizer 故意很窄。它不会去 entity-encode TypeScript 里的 data URI——在那里,HTML entity 会变成真的脏数据;它也绝不会把正文里的旧词偷偷转义掉。测试锁住了几条真正有用的保证:JSON 解析值相等、属性解码值相等、重复运行不再变化,以及普通正文仍在时守卫必须失败。

这里最细、也最重要的一条原则是:字节干净和语义相等,是两个独立断言。安全迁移必须同时证明二者。「搜索零命中」不够——如果产物已经解不回同一个东西,那只是把品牌问题换成了数据损坏。

真正的公司官网,也需要内容 schema

这次清理还暴露了第二个问题。我们的营销官网堆进了实现说明、发布状态式语言和重复解释。它经常在回答「团队刚改了什么」,却没有先回答「这家公司提供什么、适合谁、凭什么值得信任」。

我们把这些职责拆开:

问题答案应该住在哪里不该混进来的东西
产品能做什么?首页与产品内部架构和上线过程
价格和边界是什么?价格与法律模糊承诺和整段照抄的免责全文
谁在运营,怎么获得帮助?公司、支持、联系团队日志和测试证据
一次发布改了什么?产品更新长期有效的产品说明
哪条经验值得别人复用?博客「然后我们又改了这个文件」式流水账

最后一行正是这篇文章存在的原因。构建日志记录活动;有价值的博客提炼出陌生人也能带走的机制。官网变短,不是因为信息消失了,而是因为每一种信息终于只有一个合适的家。唯一刻意的例外:涉及信任的关键边界——练习筹码、不涉真钱——会在用户做决定的每个触点重复出现,并由测试钉住。

让新词汇变成可执行约束

写在文档里的命名指南,忙起来一定会漂。迁移的最后一步,是把品牌语言守卫接进 pre-push 和 CI。它检查仓库盘点范围内的文件名与原始字节;对文本给出逐行结果;对支持的机器数据检查是否使用规范表示;对二进制命中则报告文件与 offset 并失败。

品牌守卫不是官网唯一的契约。另一道营销事实检查会把公开价格、商店可用性、Solver 边界与受发布开关约束的功能,逐项对照代码和规范状态源。命名正确与事实正确是两条不同约束:一张页面完全可以只用自有语言,却仍然在事实层面说错。

错误信息不只说「不行」,还直接给出允许的去向:内部标识符使用 Table2/table2,面向用户的文案使用「新版牌桌」。这种正向词汇表很重要。只有黑名单、没有目的地,只会教下一个贡献者发明第三个名字。

inventory = 已跟踪文件 + 未被忽略的未跟踪文件

逐文件:
    文件名含停用语言 -> 拒绝
    原始字节含停用序列 -> 拒绝
    如果是文本:
        机器数据必须使用规范表示
        剩余正文按行报告

这道闸门故意做得朴素、确定。没有大模型在每次提交时临场判断「这个名字是不是太像别人的品牌」。政策由人审,仓库只负责每次都执行那个已经审过的结果。

诚实的边界

  • 仓库盘点不等于整个世界。Git 历史、被忽略的本地文件、旧备份、外部缓存,以及工作树之外的知识存储,都需要单独的迁移和保留决策。
  • 一个字节命中不一定是语义提及。压缩资产会出现随机碰撞,所以守卫报告 offset,修复也必须保住产物的渲染语义(靠人工核对),而不是假装每个命中都是文案。
  • 词汇干净不能证明设计原创。命名卫生无法替代视觉设计审查、商标审查或来源审计。
  • 规范名称以后仍然可以变。契约让下一次变化变得显式、可审;它并不把今天的 schema 永久冻结。

小结

  1. 可以借鉴布局,不能继承词汇。视觉参考不等于可以顺手带走另一个产品的品牌语言和内部分类。
  2. 把品牌语言当成 schema。编辑前先按受众定义规范值:朴素的用户语言、稳定的内部标识符、诚实的内容标签。
  3. 迁移整张依赖图。文件名、import、路由、测试、证据、文档、文案和生成数据,是互相连接的消费方,不是互不相干的搜索结果。
  4. 分别证明字节和含义。等价编码与谨慎的资产重导出,既要清掉原始碰撞,也不能改变解析值或渲染结果。
  5. 把契约接进 pre-push 和 CI。真正完成的清理,是旧语言无法在下一次发布里悄悄长回来。