Refactoring Philosophy
重构是手术不是修补——数学拓扑优先于编译器妥协。
框架是领域数学的固化。
框架不是"方便用户的工具集",而是强制性的数学结构——它规定了领域内概念的拓扑位置、依赖方向、暴露边界。正如欧几里得几何不会因为学生觉得难而修改公理,框架也不应该因为下游"已经在用"而保留泄露。
"不要考虑下游"听起来充满破坏性,但真正的原因是: 下游代码是框架泄露的灾难性结果——它们根本不应该被写出来。
如果为了"下游还在用"而妥协,就永远无法:
- 从下游脱身
- 学习如何将非特异性代码全部集中到框架能力中
- 获得框架的自由抽象权力
正如医生做手术不是为了伤害病人,而是为了治疗疾病:
- 伤害代码不是为了破坏编译
- 而是让顽固的代码疾病彻底治愈
医生不会在剪刀钳子和肿瘤还在患者肚子里的时候,就匆忙选择缝合伤口。
不会半途编译来假装没有错误。在手术中:
- 唯一需要检查的是生命体征平稳(核心模块能否编译)
- 而不是肚子上有没有洞(下游是否编译通过)
不是在遵从编译器工作,而是在遵从数学拓扑结构工作。
推翻旧结构时必然遭到编译器的阻拦,但那是因为编译器愚蠢而不理解数学——它只是忠诚地不让我们破坏结构。
考虑问题的顺序:
- 框架对纯粹领域内数学拓扑的固定和强制力
- 去掉任何不必要的公开可见性
- 去掉任何逻辑上有更优解而不必存在的代码
- 最后才考虑编译
公理:重导出几乎是自己承认"代码命名空间分布违背数学拓扑"。
如果概念 X 需要从 deep::path::X 重导出到 shallow::X,那么 X 从一开始就应该定义在 shallow 层。
执行:
- 在哪个命名空间,就去哪个命名空间使用
- 不期望表层导出
- 框架内部零重导出
语法糖和便利重导出只应该在最最最表层提供给最外边的用户。
作为框架的核心开发者,给自己提供 sugar 毫无意义——我们本来就理解框架。
在框架纯化完成之前:
- 依赖不良抽象的下游视为扩散的癌症
- 其代码死活与上游无关
- 上游需要封闭地、自私地工作
公理:大型重构是外科手术,不是门诊输液。手术需要无菌环境。
执行规则:
- 安全同步点:在开始重构前,确保所有测试通过并提交一个干净的 commit。这是手术的"麻醉前检查"。
- 原子性:一次重构只做一件事。可以是大件事,但必须是一件事。
- 隔离:重构期间不修复无关 bug,不添加无关功能。手术室里不处理感冒。
- 验证:重构完成后必须全量测试通过才能提交。这是手术的"术后检查"。
- 回滚能力:任何时刻都可以通过
git reset --hard回到安全同步点。手术失败可以缝回去。
手术是完整的。在手术中:
- Core 模块必须始终能编译(生命体征监测)
- 下游编译错误是预期的、正确的(手术切口)
- 不在手术中间休息、提交、或假装完成
每次重构完成后记录:
- 数学动机(为什么必须做)
- 结构变更(做了什么)
- 验证结果(测试全量通过)
- 已知残留(如有)
不良实现不是资产,是负资产。对依赖负资产的下游坚持兼容是错误的。
- 对不良实现做重构时不考虑向前或向后兼容性
- 拒绝以兼容性为目的但本质是放任技术债务的设计
- 追求最佳实现,彻底执行
兼容性仅在以下场景合理:
- 良好实现之间的版本过渡
- 公开稳定 API 的有计划演化
- 有明确时间线的废弃路径
兼容性不合理的场景:
- 保护依赖泄露的下游代码
- 拖延已知的设计缺陷修复
- 害怕破坏编译
框架是领域数学的固化,不是便利工具的堆砌。
破坏下游是治愈泄露的必要代价。
我们遵从数学拓扑,不遵从编译器。
手术在干净的无菌环境中进行,有安全的回退点。