翻这套工业数据 WebApi 的目录时,我盯着一个叫 .Previous 的文件夹看了很久。
它不是一两个,是成片地留着。全仓算下来,整插件的旧版本目录有 6 个,插件内部还藏着 23 个旧版子目录,加在一起 29 处旧代码还躺在构建里。ts01 那篇写过"为什么删不掉",我这篇想说另一面:为什么"不敢删"。
(等价改写,名字换掉了)其中一个整插件旧版,大致是这样被新加载器指着:
// 新框架仍然加载 Contoso.Business.Previous 里的旧实现
_context.LoadFromAssemblyPath(
Path.Combine(pluginDir, "Contoso.Business.Previous.dll"));
引用链没断
我试着顺着其中一个 .Previous 往上找,发现新代码里还有调用方直接指着它的入口类。删掉这个目录,那一方立刻编译不过。换句话说,旧版不是孤立的尸体,它还在被活着的代码牵着。
更麻烦的是这些引用藏在插件边界后面。一个插件引用另一个插件的旧实现,依赖关系是隐形的,光看目录根本看不出谁在用它。我没法确认当年每一处保留的动机,但更可能的情况是:某次迁移只迁了一半,旧版先留着顶着,等哪天有空再断链——然后就没有哪天了。
代价是沉默的
这 29 处旧代码不报错。它们只是让每一个新人进来的第一件事变成:先搞清"现在到底用哪个版本"。这个判断没有任何文档兜底,只能靠问老人,问着问着老人也忘了。
它还会把迁移钉死。旧版依赖的某些接口在新框架里已经不推荐,只要引用链没断,整体就往前走不动。6 个整插件旧版,等于 6 个被按下暂停的迁移任务,23 个内部子目录是更小的同样故事。
断链比删除难
我后来没敢一把全删,而是给每个 .Previous 做了一张"谁还在指它"的引用表,按引用数从少到多逐个断。断一个、编译一遍、跑一下插件加载测试,确认没方报错,再删目录。内部那 23 个子目录同理,先确认新路径已经覆盖,才动手清。
清理之后,主方案里不再有带 .Previous 的工程,新加载器只认当前版本。但真正难的不是删,是承认:这些文件夹从来不是"忘了删",是"暂时不动"的决定堆成的。技术债不一定会报错,它只是让下一步永远差一点点。