【技术】【开发者成长】升级遗留代码的最佳实践( 二 )


选项一:大爆炸式的重写
第一个也是最明显的选项 , 就是大爆炸式的重写:从头开始更改代码库 , 并在一次转换中切换所有用户 。 但是 , 完全重写是非常耗时的 , 而且必然也会产生相当巨大的成本 。 你也有可能最终得到的是一款在几个月甚至几年内都不适合发布的应用程序 , 并且在这个过程结束之前 , 你都无法看到最终结果 。 另外 , 应用程序越大 , 开发者在重写过程中提供维护和添加新功能的难度就越大 。 如果有文档和技术知识的话 , 向大型代码库添加新功能就像在公园里散步一样简单 。 若没有这些的话 , 要做到这些 , 真的很难 。
选项二:凤凰涅槃
第二个选项是在现有代码库中添加使用新技术构建的新功能 。 理想情况下 , 你不应该触及旧技术 , 而应该将所有新功能分离开来 。 但不幸的是 , 这样的原始结果相当罕见:新功能几乎总是需要与旧功能集成 。 这也需要一个详细的计划 , 因为事情很复杂 , 没有周密计划的话很难做好 。 在复杂的架构中创建新的组件 , 需要大量关于遗留应用程序中组件运行情况的信息 。 你需要一个广阔的视角 , 从新旧两个角度看待这项技术 。
使用单体应用程序 , 你可以在单独部署的新代码库中创建新功能 , 同时使用与新代码和遗留代码交互的单个数据库来存储数据 。 这个解决方案看起来很简单 , 但它能否取得长期成功 , 要取决于你的承诺是否坚如磐石 。 (特别是当你的整体系统受到机器性能或并发性问题的影响时 , 就需要考虑这些问题 。 )例如 , 如果你的单体应用程序开始获得更多的用户 , 那么 , 单个数据库可能会成为瓶颈 。 (另一方面 , 云端中的数据库可以扩展 。 )由于原始代码库的长期脆弱性 , 特别是考虑到潜在的安全漏洞或 bug , 这种架构并不能持久 。 实际上 , 让过时的代码保持原样就意味着你在等待它最终永远失败 。
选项三:混合方法
重写整个代码库是一个极端的想法 , 而且往往考虑不周 。 在旧代码库上添加新功能更可行 , 但会带来严重的副作用 , 例如 , 如果你的遗留代码是基于较旧的框架版本 , 那么就会出现安全问题 。 那么 , 还有什么事情是你可以做的 , 而且不那么昂贵 , 或者不那么危险的? 你还有其他选择吗?
我推荐一种混合方法 。 这一选项 , 就像大爆炸式重写一样 , 也需要对整个旧代码进行变更 。 但与第一种选项不同的是 , 重写应该是在一段时间内展开(比如说几年) , 以最大限度地减少技术债务和财务成本 。 这种渐进式方法将基于你的愿景 , 这一点至关重要 。 你需要知道首先要改变什么:有些功能是核心的 , 对业务至关重要 , 而其他功能则更多的是扮演辅助角色 。 有了明确的目标 , 在多个层面上工作就会变得更容易 。 例如 , 你是否仍然计划在这个转换过程中发布一项新功能?如果是这样的话 , 你就需要在计划中考虑到这一点 。 如果没有清晰的路线图 , 你的代码最终将会变得一团糟 , 这将会使开发者更难理解 。
对我来说 , 这种方法关乎未来和可扩展性:而且它最容易使用微服务来实现 。 假设你的遗留代码库是新的微服务生态系统中的一个元素 。 当然 , 它太过于庞大 , 过于复杂 , 不可能是真正的微服务 , 但它可以像微服务一样与新功能进行通信 。 为了处理这种安排 , 你需要创建 API 或“桥”这样的接口 , 以允许遗留代码与新技术进行通讯 。 当你以单独的微服务形式添加新功能时 , 它们将会一点一点地吞噬遗留代码的业务逻辑 。 虽然你可以通过向遗留的单体应用程序添加新功能来实现类似的功能 , 但此举可能会造成技术债务 , 并失去灵活性 。
几乎所有的解决方案都有副作用 , 包括这种方法 。 但是我们需要知道如何将副作用最小化 。 对于 Web 应用的前端 , 反向代理可以缓和这一变更带来的副作用 。 使用这种方法 , 你甚至可以在不触及遗留软件的情况下 , 替换 Web 应用中为单个 URL 提供服务的逻辑 。 但这种技术有其自身的要求 , 例如 , 如果你有用户登录的话 , 就应该维护页面之间的状态 。 我们始终需要存储状态 , 但在这个解决方案中 , 我们需要在两个应用之间移动或共享状态 。 这很难维护 , 但你还是可以得到更具弹性的基础设施 。