可维护性
代码被阅读的次数远多于编写的次数。可维护性是让一个三年历史的代码库令人愉悦还是如同焦油坑的分水岭。要为下一个开发者优化,他可能六个月后的就是你。
规则
- 一个特性,一个位置。 按特性打包(参见 backend/core/SKILL.md)。单一变更应触及相邻文件,而非分散的层次。
- 仅在模块边界使用接口。 在特性内部,具体类就很好。接口的存在是为了让调用者跨边界依赖契约,而不是为了装饰每个类。
- 删除,不要注释掉。 死代码和
// TODO永久的注释会腐烂。Git 记得住;IDE 不需要。 - 优先使用小型纯函数。 更容易测试、更容易推理、更容易组合。副作用位于系统边缘,而不是散落在各处。
- 测试覆盖行为,而非实现。 重写内部实现不应破坏测试。如果破坏了,说明测试耦合到了错误的东西上。
- 公开 API 应有版本。 破坏性变更跟在新的版本或特性标志后面——永远不要默默重命名。
- 依赖注入优于静态单例。 静态不可测试且隐藏耦合。传入依赖;在测试中模拟它们。
- 锁定并定期更新依赖。 锁定文件已提交;Renovate/Dependabot 自动创建 PR。落后两年意味着一个计划外的两周迁移。
- 记录“为什么”,而不是“是什么”。 代码展示了是什么;注释解释业务规则、历史约束、非显而易见的权衡。
- 在 CI 中执行 lint、格式化、类型检查。 不是建议性的,而是阻断性的。人类应争论设计,而非空格。
- 保持构建绿色且快速。 不稳定或慢的测试会被忽略,一旦被忽略,它们就保护不了任何东西。
红牌标志
- 单个文件超过约 500 行,或一个类有超过约 10 个公共方法——通常是某个特性试图逃离的迹象。
- 共享的 "utils" 包,孤儿般的辅助函数在这里走向死亡。
- 为了通过测试而模拟被测试对象本身。
- “别碰那个模块,没人再理解它了。”
- CI 构建时间以数十分钟计。