代码推倒重写_保留旧版代码还是重写? 中间方式

代码推倒重写

We began to write our main product in 2013. That was just our second Single Page Application (SPA) ever. The first one was a small project, we analyzed the smaller experience to avoid repeating errors we already made.At the time, the browser and js libraries landscape was different, our enterprise target included IE9 users and we were not confident about big and complex frameworks so we preferred to adopt a set of separated libraries. We built our own framework: flexible, easy to use, and with just the features we wanted such as data binding, templating, routing, internationalization, …For each missing feature, we were free to choose the preferred library, with its pro and cons. It was a case of “too much freedom of choice”.

我们从2013年开始编写我们的主要产品。这只是我们第二个单页应用程序(SPA)。 第一个是一个小项目,我们分析了较小的经验以避免重复我们已经犯的错误。当时,浏览器和js库的情况有所不同,我们的企业目标包括IE9用户,我们对大型复杂框架不抱有信心因此我们更喜欢采用一组分离的库。 我们构建了自己的框架:灵活,易于使用,并且仅具有我们想要的功能,例如数据绑定,模板化,路由,国际化...……对于每个缺失的功能,我们可以自由选择首选的库,其优缺点。 这是“选择自由太多”的情况。

Part of our journey in splitting the monolith application is available in other blog posts: code repository impacts, frontend code architecture.

其他博客文章中提供了拆分Monolith应用程序的部分旅程: 代码存储库影响前端代码体系结构

As time went by, we added many features to our application, we become more skilled, the tech landscape evolved but, most of all, the application become bigger and bigger. We tried our best to keep the highest code quality we could but, as you know, any developer that reviews his code after many years wants to burn it and rewrite it. With a big SPA, this is just not possible because of the risk and the size of the task itself. We had to find a different way to satisfy our new requirements:

随着时间的流逝,我们在应用程序中添加了许多功能,我们变得越来越熟练,技术领域也在不断发展,但最重要的是,应用程序越来越大。 我们尽力保持最高的代码质量,但是,正如您所知,多年后审查其代码的任何开发人员都希望对其进行刻录和重写。 对于大型SPA,由于风险和任务本身的规模,这是不可能的。 我们必须找到另一种方式来满足我们的新要求:

  • we wanted to be able to add new sections to the app without affecting performance. Our SPA did not follow “code splitting” so each new interface was added to the single fat chunk of code. We have been focused on adding features for too long and when we realized that it was the moment to work on code-splitting, it was too late: it was all too tightly coupled to be split.

    我们希望能够在不影响性能的情况下向应用程序添加新部分。 我们的SPA不遵循“代码拆分”的原则,因此每个新接口都被添加到单个繁琐的代码块中。 我们专注于添加功能的时间太长了,当我们意识到这是进行代码拆分的时机,为时已晚:所有内容都紧密耦合在一起,无法拆分。

  • we wanted to be able to adopt modern and faster frameworks but, more important, be free to update or change them over time. For many complex interfaces, 7 years old libraries don’t deliver the same performance that can be achieved by using modern ones that leverage, for example, Virtual DOM.

    我们希望能够采用现代,更快的框架,但更重要的是, 可以随时更新或更改它们。 对于许多复杂的接口,已有7年历史的库所提供的性能无法达到使用诸如Virtual DOM之类的现代库所能实现的性能。

  • By collecting data on our analytics we realized the market was mature enough to reduce the scope of the supported browsers to IE11+. This opens up possibilities that were just not possible while keeping IE9+ support.

    通过收集分析数据,我们意识到市场已经足够成熟,可以将支持的浏览器范围缩小到IE11 + 。 这为保持IE9 +支持开辟了可能性。

  • We wanted to be able to split code development and maintenance across different team members working at the same time. Business does not slow or stop, so we still want to be able to push out new features at a high pace, but we want to be able to do that by using new libraries, so we had to find a way to make old code coexist with new code.

    我们希望能够将代码开发和维护分散 到同时工作的 不同团队成员之间 。 业务不会减慢或停止,因此我们仍然希望能够快速推出新功能,但是我们希望能够通过使用新库来做到这一点,因此我们必须找到一种使旧代码共存的方法新代码。

  • We wanted an easier and more agile development. In the current architecture, a developer that needs to add a section might have to learn about the whole architecture, to ensure they don’t introduce regressions. The solution we are looking for must allow for better decoupling of components and to let each developer reduce their learning scope to just the areas they have to work on.

    我们想要一个更轻松,更敏捷的开发 。 在当前体系结构中,需要添加一个部分的开发人员可能必须了解整个体系结构,以确保不引入回归。 我们正在寻找的解决方案必须允许组件之间更好的去耦,并让每个开发人员将学习范围缩小到他们必须从事的领域。

But how can we reach all these objectives without stopping product evolutions? It would be impossible to rewrite the entire application for a single release.

但是我们如何在不停止产品发展的情况下实现所有这些目标呢? 不可能为单个发行版重写整个应用程序

我们使用的方法 (The approach we used)

We split the work into three stages:

我们将工作分为三个阶段:

步骤1:从头开始,就像我们要重新设计一切一样 (Step 1: start from scratch, like we were going to redesign everything)

We tried to break up with the current application and start to imagine a new architecture from scratch, looking at popular frameworks and famous applications to get inspiration.

我们试图与当前的应用程序分离,并开始从头开始想象一个新的体系结构,通过查看流行的框架和著名的应用程序来获得灵感。

Then we created a Proof of Concept (POC) solution to validate our requirements and while we were developing it we added another requirement: it has to provide section-management framework independency. It means that we wanted to avoid being tightly tied to a specific framework. A time will come when we will want to adopt a new framework for special futures or improve maintainability, and we want to be able to do so without refactorings.

然后,我们创建了概念验证(POC)解决方案来验证我们的要求,并且在我们开发该解决方案时,我们添加了另一个要求:它必须提供节段管理框架的独立性。 这意味着我们要避免与特定框架紧密联系 。 我们将需要采用一个特殊期货的新框架或提高可维护性的时候到了,我们希望能够在不进行重构的情况下做到这一点。

The POC idea was to have a “loader” that could import each section with the lowest amount of dependencies possible, up to the point that we wanted to import sections written in different frameworks. The old application, in this case, would just become a section to be loaded.

POC的想法是要有一个“加载器”,该加载器可以以尽可能低的依赖关系量导入每个节,直到我们要导入用不同框架编写的节为止。 在这种情况下,旧的应用程序将成为要加载的部分。

This was the only way we found to modernize our application without stopping product evolution: consider any old interface as a special section of our new and shining application.

这是我们发现的不间断产品升级而对应用程序进行现代化改造的唯一方法:将任何旧界面视为新的应用程序的特殊部分。

With these requirements, the core of the POC has become a “section manager” that grows around the Vue Router and its powerful navigation guards. When a user asks a section we check its capability and load the right section chunk.

有了这些要求,POC的核心就变成了一个“分区管理器”,它围绕Vue Router及其强大的导航保护器而发展 。 当用户询问某个部分时,我们会检查其功能并加载正确的部分块。

The old application structure can be summarized as:

旧的应用程序结构可以总结为:

legacy code, loading process old application
The loading process of the former application
前一个应用程序的加载过程

To allow the old application to be loaded as a module some changes needed to be made:

为了允许将旧应用程序作为模块加载,需要进行一些更改:

  • The old application bootstrap was a lightweight module that manages basic behaviors about application startup such as resources loading clip, mobile application redirect, brand colors, security preferences. This was the only part of our SPA that has been totally rewritten. Those were not too many statements, so we also took the chance to remove deprecated logic.

    旧的应用程序引导程序是一个轻量级的模块,用于管理有关应用程序启动的基本行为,例如资源加载剪辑,移动应用程序重定向,品牌颜色,安全偏好。 这是我们SPA中唯一被完全重写的部分。 这些语句不是太多,因此我们也借此机会删除了过时的逻辑。
  • The old application used the entire HTML body to render the interfaces. This approach had to be changed so that it could work on a given HTML Element instead of the whole body section.

    旧的应用程序使用了整个HTML主体来呈现接口。 必须更改此方法,以便可以在给定HTML元素而不是整个正文部分上使用。
  • We were forced to change the navigation API. The legacy application used window.location.hash instead of modern window.history.pushState, used by Vue Router of the section loader. In the first phase we tried to make them work together but, adding the IE11 support, we obtained a “monster” that was not even working consistently. The only solution we found was to inject the reference to the main router into the just-loaded section so that the entire application navigation uses the Vue router.

    我们被迫更改导航API。 旧版应用程序使用window.location.hash代替了部分加载器的Vue Router使用的现代window.history.pushState。 在第一阶段,我们试图使它们协同工作,但是,添加了IE11支持,我们获得了一个“怪兽”,它甚至不能始终如一地工作。 我们发现的唯一解决方案是将对主路由器的引用注入到刚刚加载的部分中,以便整个应用程序导航都使用Vue路由器。

We then stumbled into a problem: the new application was allowing us to create only news sections, which was the smallest entity that could be created and we thought that it might have been just too large in some areas. Why did we just stop at the “section level”?

然后,我们陷入一个问题:新的应用程序允许我们仅创建新闻栏目,这是可以创建的最小实体,并且我们认为在某些区域可能太大。 为什么我们只停留在“部门级别”?

We then identified the three different scenarios that we wanted to be able to manage with the new architecture:

然后,我们确定了我们希望能够使用新架构进行管理的三种不同方案:

Image for post

Creating new sections of the application is not very common, adding new subsections is much more frequent:

创建应用程序的新部分不是很常见,添加新子部分的频率要高得多:

  • We made it possible to write a subsection of a legacy section, in modern technology, using a nested router-view used only from new interfaces, helped by the magic of CSS.

    在CSS的帮助下,我们可以使用仅在新接口中使用的嵌套路由器视图,在现代技术中编写传统节的子节。
  • We also planned for a way to build components, written with new technologies, that could be embedded both in legacy and new sections. We used an embed-like approach, each component has an init method that is invoked by passing the DOM element that includes the component.

    我们还计划了一种构建用新技术编写的组件的方法,该组件可以嵌入旧版和新版块中。 我们使用了类似嵌入的方法,每个组件都有一个init方法,该方法通过传递包含该组件的DOM元素来调用。

this, in a hindsight, was a very good idea since we did find cases that allowed us to develop in a modern way:

事后看来,这是一个非常好的主意,因为我们确实找到了可以使我们以现代方式发展的案例:

Image for post
The search area is an example of a component that has been used in different sections or different applications
搜索区域是已在不同部分或不同应用程序中使用的组件的示例

步骤2:更改代码库结构 (Step #2: change the code base structure)

At the beginning of our SPA, we had the nice idea of organizing our source code by dividing the controller from the view. Over time we realize that by adding a simple grouping by section we could improve source navigation and project startup for a new developer.

在SPA的开始,我们有一个很好的想法,即通过从视图中划分控制器来组织源代码。 随着时间的流逝,我们意识到,通过按节添加简单的分组,我们可以为新开发人员改善源导航和项目启动。

So we started from the repository organization:

因此,我们从存储库组织开始:

  • host standard Vue project structure to ease the understanding for developers new to the codebase or the company (why we choose VUE);

    托管标准Vue项目结构,以简化对代码库或公司新开发人员的理解( 为什么选择VUE );

  • separate the resources by section. Legacy code has been relegated to a specific folder and our objective is to drain code from there (when we refactor) and reach a point where we will have an empty folder: all legacy code will be translated into a new one.

    按部分分开资源。 旧版代码已降级到一个特定的文件夹,我们的目标是从那里耗尽代码(当我们重构时)并达到一个空文件夹的位置:所有旧代码都将转换为一个新文件夹。

步骤3:交换技术 (Step #3: switch technology)

The experience gathered by working on the POC proved to be precious. We leveraged such experience to write the real loader and run our legacy application as a simple module inside the new loader.

实践证明,通过POC收集的经验非常宝贵。 我们利用这种经验编写了真正的加载器,并在新加载器中将旧应用程序作为一个简单模块运行。

But not everything went smoothly. If we had the chance to rewrite the whole legacy part we could improve performance greatly, instead we kept the same performance as the old code when loading the legacy part. End-users did not benefit from this upgrade.

但并非一切都顺利。 如果我们有机会重写整个遗留部分,我们可以极大地提高性能,相反,在加载遗留部分时,我们可以保持与旧代码相同的性能。 最终用户没有从此升级中受益。

One of the reasons why we did not feel safe in undertaking a whole rewrite is that not all sections are widely covered by an automated test suite. Not having an automated test-suite opens up just too many chances to miss specific edge cases or usage patterns that might lead to new regressions in the code. This is something we learned over time: tests greatly improve your confidence in refactors because while human memory can be lost, tests are forever.

我们对整体重写感到不安全的原因之一是,自动化测试套件并未涵盖所有部分。 没有自动化的测试套件会导致太多机会错过特定的极端情况或使用模式,这可能会导致代码中出现新的回归。 这是我们随着时间的推移而学到的东西:测试可以极大地提高您对重构的信心,因为虽然可能会丢失人的记忆,但是测试却永远存在。

结论 (Conclusions)

In the end, adopting modern JS frameworks proved to be a remarkable improvement: developers can stay focused on application logic, and not on low-level details. For each new feature, we now have less code to maintain and better performance for our customers. Legacy code and new code co-exist in a framework independent mix of components that build our SPA.

最后,事实证明,采用现代JS框架是一项了不起的进步:开发人员可以继续专注于应用程序逻辑,而不是底层细节。 对于每个新功能,我们现在需要维护的代码更少,并且为客户提供了更好的性能。 旧代码和新代码共存于独立于框架的各种组件中,这些组件共同构成了我们的SPA。

The new architecture also brought us other improvements:

新架构还为我们带来了其他改进:

  • Simpler architecture makes it easy to implement optimizations. It is now much easier to take a big section and split it into smaller chunks, and easier to maintain.

    更简单的体系结构使实现优化变得容易。 现在,将一个较大的部分分割成较小的块变得更加容易,并且更易于维护。
  • Developers never feel like “no one can hear you scream in the space”: modern frameworks mean lively and helpful communities.

    开发人员永远不会感觉到“没人能听到你在太空中尖叫”:现代框架意味着活跃而有用的社区。
  • Less legacy code means less “black boxes”, more code is accessible to a wider developer community, improving the quality and the maintainability.

    更少的遗留代码意味着更少的“黑匣子”,更多的代码可供更广泛的开发人员社区使用,从而提高了质量和可维护性。

The next step will be to improve the test suite.

下一步将是改进测试套件。

Have you ever had to force the co-existence of legacy code with modern frameworks? How did you choose to do it? Please let us know in the comments below.

您是否曾经不得不迫使遗留代码与现代框架共存? 您是如何选择这样做的? 请在下面的评论中告诉我们。

翻译自: https://medium.com/thron-tech/keep-legacy-code-or-rewrite-a-middle-way-dc77c2f76e5f

代码推倒重写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值