15 · 架构模式、代价与启示
Mole 的优秀之处不在“规则多”,而在于把不确定性推向显式状态;它的主要代价也来自规则与双运行时不断增长。
模式一:危险动作前的最后一公里验证
Clean 的 process guard、Purge 的活跃度复检、Installer 的 inode/size 复核、sudo 的祖先可变性检查,都是同一模式:不要只验证计划,必须验证将要执行的对象。
可迁移到部署、迁移、批量编辑工具:计划中保存对象身份和关键不变量,执行前重新读取并比较。失败时跳过并报告,不猜测。
模式二:可恢复性是 API 契约
Trash 失败不降级 permanent;Analyze 根本不暴露 permanent;高权限 Trash 失败保留 staging 路径。恢复语义贯穿 UI、执行与日志,不能只是一种底层实现偏好。
这同样适用于数据库迁移与配置工具:用户选择“可回滚”后,任何无法保证回滚的分支都应停止,而非继续“尽量完成”。
模式三:结果是代数,不是布尔值
Optimize 的六态模型比 exit code 更准确;Uninstall 区分删除、跳过、review-only;Installer 能表达部分未完成;Status 的指标按鲜度分层。将现实中的状态显式建模,汇总才不会撒谎。
模式四:身份优先于名称
Bundle ID、device+inode、PID+PPID+command、cache schema/version 都是在解决同一问题:文本标签可能复用,真正的对象身份需要更强证据。
模式五:快速路径必须有保守降级
Spotlight、缓存、并行 du、预构建二进制都只是加速器。不可用时可以退到直接 Info.plist、递归扫描或源码构建;但若快速路径返回“验证失败”,不能把它误当作“不支持”然后跳过安全门。
主要代价
1. 规则维护负担
macOS 路径、应用 Bundle、EDR、云盘与开发工具持续变化。数据表分离提高可审查性,却不能消除规则陈旧。Bundle audit workflow 和回归测试只能降低风险。
2. Shell 规模与隐式状态
Bash 3.2 提供极佳的系统集成,却依赖全局环境变量、source 顺序、trap 和对齐数组。MOLE_UNINSTALL_MODE、MOLE_DRY_RUN 等状态跨函数传播,需要严格命名和测试。继续扩展时,策略对象可能值得迁移到更强类型的内部工具,但删除汇点的系统细节仍未必适合完全离开 Shell。
3. Shell 与 Go 语义重复
两套 Trash 与保护规则能针对各自运行时优化,也带来漂移风险。例如一侧新增 EDR 路径,另一侧若遗漏就产生产品不一致。更实际的治理方式不是强行共享代码,而是共享测试向量和行为契约。
4. 可观测性仍以本机日志为主
这符合隐私与本地工具定位,但跨机器诊断主要依赖用户导出 JSON/日志。日志 schema 与兼容性因此应被当作公共接口管理。
最值得带走的审查清单
- 发现器漏报是否安全?误报是否会被下游阻止?
- 用户确认后,对象身份还能变化吗?执行前检查什么?
- 可恢复路径失败时会不会偷偷进入不可恢复路径?
- “无变化、跳过、不可用、需关注、失败”是否被压成一个成功码?
- 缓存和快速路径失效时,是保守降级还是绕过验证?
- 测试是否可能触发宿主机真实副作用?
结论
Mole 展示了一种适合本地系统工具的架构:薄路由、丰富发现器、集中保护层、重复验证的副作用汇点、明确结果模型,以及与发布供应链相连的验证证据。它并不追求所有模块形式统一,而是让风险边界统一。
下一章提供按文件和按任务组织的源码地图,方便把本书结论带回代码。