稳定优先:大型项目中遗留代码共存策略
在长期运行、高负载的系统中,删除遗留代码可能引发回归问题和系统不稳定。与其进行大规模重构,不如采用功能开关和门面模式,让新旧代码并行运行。这种做法支持持续开发而无需停机,同时保留经过验证的组件作为故障回退方案。
激进代码删除的风险
即使移除已废弃的代码,也可能因隐藏依赖(如反射、动态加载、RPC调用)导致失败。遗留系统的测试通常无法覆盖边缘场景,而生产数据常会触发罕见执行路径。更严重的是,组织知识流失加剧了风险——离职团队留下关键代码,用于迁移或合规等特殊场景。
在企业环境中,厂商定制模块尤为脆弱,任何变更都可能导致厂商支持失效。
实践中的“不碰老系统”原则
一个经典案例是NASA的深空一号探测器:1970年代的代码被完整保留,并通过适配器封装。只要现有组件运行稳定,就不应轻易改动。这一成熟策略特别适用于运行超过五年的系统。
共存架构:反向藤蔓模式
不要摧毁旧系统,而是并行构建新系统,逐步将流量转移。两种策略:
| 策略 | 操作 | 适用场景 |
|----------|---------|----------------|
| 删除 | 在新架构上重写 | 逻辑简单、测试覆盖率100%、无依赖 |
| 共存 | 新代码与旧代码并行,渐进式路由 | 核心功能、测试薄弱、风险高 |
功能开关保障安全:
- 开发新实现。
- 包裹在标志判断中。
- 在1%流量上运行。
- 成功则逐步扩大。
- 出现问题可立即通过标志回滚。
遗留代码在仓库中保留2–3年,用于可靠性分析。
Spring Boot 中的实现
将遗留代码封装在接口中,不修改原始代码。
// 遗留服务(保持不变)
@Service
public class LegacyDiscountService {
public double calculateDiscount(Order order) {
// 五年前的逻辑
return order.getTotal() * 0.1;
}
}
// 新服务
@Service
public class NewDiscountService {
public double calculateDiscount(Order order) {
return applyComplexRules(order);
}
}
带开关的门面:
@Component
public class DiscountServiceFacade {
private final LegacyDiscountService legacyService;
private final NewDiscountService newService;
@Value("${feature.discount.new.enabled:false}")
private boolean useNewService;
public DiscountServiceFacade(LegacyDiscountService legacyService,
NewDiscountService newService) {
this.legacyService = legacyService;
this.newService = newService;
}
public double calculateDiscount(Order order) {
if (useNewService) {
return newService.calculateDiscount(order);
}
return legacyService.calculateDiscount(order);
}
}
通过配置文件隔离:
@Configuration
public class DiscountConfiguration {
@Bean
@ConditionalOnProperty(name = "feature.discount.new", havingValue = "false", matchIfMissing = true)
public DiscountService legacyDiscountService() {
return new LegacyDiscountService();
}
@Bean
@ConditionalOnProperty(name = "feature.discount.new", havingValue = "true")
public DiscountService newDiscountService() {
return new NewDiscountService();
}
}
测试排除遗留代码:
@Test
@DisabledIf("!${feature.discount.new.enabled}")
void testNewDiscountLogic() {
// 仅测试新逻辑
}
流量切换工作流
API网关根据标志路由:单体架构作为后备,微服务逐步扩容。功能开关服务实时管理流量比例。遗留代码在生产环境保留2–3年。
删除不可避免时的应对
- 安全:过时库存在CVE漏洞。
- 合规:违反GDPR等法规。
- 技术终结:无补丁支持的Java版本。
- 无人可用:缺乏相关领域专家。
即便如此,也应采用渐进式发布并具备回滚能力。
核心要点
- 系统稳定性高于代码整洁度:可用性比删掉几行旧代码更重要。
- 隐藏依赖需监控真实生产调用后再决定是否删除。
- 功能开关显著降低迁移风险。
- 技术债若维护成本低于重构,则值得保留。
- 代码中承载组织记忆:未达百分百信心前切勿删除。
— Editorial Team
暂无评论。