I am currently working for NHN building Dooray!, a collaboration tool for companies. It is a 5 year old project that entails a wide range of features including Project, Mail, Messenger, Calendar, Drive, Wiki, Contacts, Kanban and etc., and is still maintained by a group of developers working arduously to provide best quality services. As for the framework, we decided to use AngularJS, the hottest framework during the initial stages of development, and to this day, most of the front-end is built using AngularJS. However, since AngularJS ended its glorious career with the release of v1.7x, with only minimum maintenance released now, our team was nervous with discussions on whether we could continue with AngularJS. Amid the anxiety, the Dooray! team decided on the VueJS as the next front-end framework. The topic of the best framework is basically a technical hot potato, and is also never ending. Therefore, instead of discussing why we picked Vue, I would like to share what I learned from migrating from one framework to another. While AngularJS still has high retention rate, I assume that there are more groups in a similar predicament as we were. Although some of the content may be obvious, I hope that this article can be of help to others when migrating between frameworks.
Since framework migration is not a process of adding new features or updating bug fixes, the program must operate identically before and after the migration. One of the best way to make certain is the regression test, and it is often a good idea to come up with the tests ensuring the original functionalities before the actual migration.
The first thing I looked for was the service document. I would say it is incredibly lucky if the original project designers and developers are still working for the same company because, then, you would easily be able to obtain the product history and functionality documentations. Also, if you run into a problem along the migration, you would be able to ask for feedback, and it is very likely that the project founders kept the original project document.
The service document is important for couple of reasons. For one, the service document includes specs for most of the features. By understanding the specs, you can determine the order, method, and duration of the implementation. Because the interface flow can lead to the dependency of the implementation order, I want to further emphasize the importance of understanding the specs. Just the mere thought of having to squeeze in a functionality because I did not have a full understanding of the specs when I began migration gives me chills.
My personal experience with this has to do with a search functionality. Because the search UI was marked as AngularJS on screen, I was not aware that I had to recreate the functionality in Vue. After already having spent many hours in development, I realized that I had overlooked a feature in which you type to AngularJS and Vue would display the search results, and the late realization thereby affected the entire development schedule. I managed to implement the correct feature with a crazed frenzy of code typing to make up for lost time, and I am willing to bet this is why one of the keys stopped working.
A screenshot of Dooray! Contacts section showing the Vue section. The Search UI implemented with AngularJS is shown in green.
If you are, like myself, in a situation where you do not have access to the service document, you would have to try and understand each and every functionality of the product like the back of your hand. So, hooray, me!
Quality Assurance test cases is yet another crucial document for a successful framework migration. While for many of you, the service document will be incredibly detailed and sufficient, QA team’s (at least the Dooray! team’s QA) test cases thoroughly validate the functionalities and the interface flow from the user’s perspective. Therefore, it provides an excellent opportunity to check for features that are not mentioned in the service document.
In my case, the QA test cases were much more precious to me because of my lack of proper service document. There were multiple times in which I learned hidden features of the project while inspecting the QA test cases. There were around 300 test cases for the Dooray! Contacts service, and the detailed test cases regarding screen-based, feature-based, and section-based documentations were of great help. So, make sure that you read the test cases while trying them out for yourself. This process can help you prevent yet another key-break scenario.
Even if you have access to both the service document and the QA test cases, you still need to understand the features from the code. As for me, I was migrating from Angular to Vue, so aside from codes native to the framework, I had to reimplement from scratch (or delicately reprogram each functional units.) The main idea I want to emphasize for this process is the case of changing UI library. You first need to consider whether the library you used for Angular also exists for Vue. Some of the active components may have been custom implemented using inheritance or forking.
The Dooray! Contacts page used Bootstrap, and as we migrated to Vue, we decided to use element-ui. Because we chose a different UI library, everything including the components’ functionality, design, and detailed orientation (transitions etc.) are subjected to change. Therefore, we had to further develop the common styling throughout the service (including the CSS override,) components that we developed in AngularJS by either forking or custom implementing, and components that did not completely suit the element-ui.
The one thing I regret the most is not having a better idea of the features prior to migration. Therefore, I reiterate the importance of service document, QA test cases, and the original code. The planning of the migration comes after it.
Because everyone has different development methodology, management system, and personal preference, I do not plan on providing a concrete “one way” to create a plan. However, I would like to comment on the idea of planning in general, and that is, as I said before, understand the original features as much as possible.
These steps should help in narrowing the difference between expectation and the result.
Near the end of the sprint, our team started the regression tests on our own before the QA. While we already requested the QA team for a full QA, so that we would have access to wider test coverage, we believed that independently testing with inside knowledge would help us cover more edge cases. We initially aimed for 95% success rate, but the actual result turned out to be 85%.
Then, after four days of QA, the QA found more than 70 issues. Upon carefully contemplating the issues, 30% of the issues were because we did not sufficiently understand the original features. These were unnecessary issues that discouraged the entire team. The entire team seemed to agree at the end of the sprint meeting.
Causes for Issues
Now that I think about it, had we spent more time trying to understand the features more thoroughly, we could have mitigated the QA disaster. However, when all things are said and done, I am content that with everyone’s arduous efforts, the Dooray! Contacts Vue migration has successfully finished.
During the planing phase, we were most worried about the resulting performance of the product. The Dooray! has been developed as an SPA, and each service was formatted in tabs as in the image below. Given that migrating the entire service to Vue at once seemed difficult in terms of difficulty, schedule, and maintenance, we decided to start with one tab at a time.
We mainly discussed three different structural designs, and each has different pros and cons. The structural design is closely related to performance. The following contents should be useful for those in similar predicaments.
The first design was to give up on the SPA, and create separate pages. Since we could simply use Vue to develop separate pages, this seemed to be relatively easy. We believed that the CSS for new implementation would be distinguished well from the original, and we could use webpack to select the ones we need. The downside is that during the page transition, the “blink” compromises the sense of one application, the benefit of having an SPA. Also, since the resources have to be loaded again, the performance would drop ostensibly. Such is not a trade we were willing to make.
The second design was to maintain the SPA, and deal with the Vue sections using iframe. This method includes all of the pros of the first method, and upon prototyping, there was no problem passing data between the main frame and the child frame. The greatest benefit of this design is that we could separate the CSS like when working with Shadow DOM. By doing so, we would have been able to eliminate unpredictable CSS collisions in the vast sea of sources. The only downside was the emotional one. We could not let ourselves work with iframes in today’s day and age, and after prototyping the last design, we gladly gave up on the idea.
The last design was to maintain the SPA, but directly inject the Vue component inside of the AngularJS. Upon research, we found ng-vue libraries, but it had restrictive usages, and we believed that we could implement it ourselves. The AngularJS has a ng-non-bindable that enables certain codes to remain in pure DOM structure if below a certain threshold. By using the directive, we can easily inject Vue into Angular as in the example below.
The ng-non-bindable in AngularJS template
<div class="contact-vue-component" ng-non-bindable>
<div class="vue-wrapper-element"></div>
</div>
Directly creating Vue within the AngularJS component controller
this.$onInit = () => {
this.component = new Vue({
el: $element.find(".vue-wrapper-element")[0],
store,
router,
components: {
ServiceMain
},
data() {
return {
state: "nice state"
};
},
template: `
<service-main component-name="ContactBodyContainer" :state="state"></service-main>`
});
};
Finally, we decided to go with the third design. As more and more areas are controlled by Vue, the AngularJS will slowly fade out, and the entire app will become a Vue application.
I decided to include the section about the structure to discuss the performance after framework migration more clearly. Unlike the first and second methods, the third method does not use a separate page, we added the new Vue resources to the existing AngularJS resources. Therefore, we were worried about the performance. To spoil the results first, luckily, it did not damage the performance.
Using the Chrome devtools’ Network, Performance, and Audits, we compared the before and after the migration.
Following are our measurements of the loading performance.
Event | AngularJS | AngularJS + Vue |
---|---|---|
DCL(DOMContentLoaded) | 1900 ms | 1922 ms |
Loaded(L) | 3856 ms | 3666 ms |
FMP(First Meaningful Paint) | 5896 ms | 3722 ms |
The DCL is a value that is affected by the size of the HTML file and the number of JavaScript and CSS files included. Since the project does not require a server-side rendering due to the SPA nature, there was no change in the size of HTML, and the changes made to the JavaScript and CSS resources did not affect the performance. The Loaded Event, despite the new resource structure, had no effect on the performance. However, the FMP, one of the main indexes used to discuss loading speed, actually had a decrease of 2 seconds. Upon careful inspection of the data, we found that the 2 seconds were reduced by eliminating the function call to Bootstrap from AngularJS. Without AngularJS’s Bootstrap call, the Vue initialization speed can be considered to be extraordinarily fast.
From the perspective of the general loading speed, there was no significant decrease in performance, and by reducing the time it takes AngularJS to call Bootstrap by 2 seconds, we can conclude that the performance is better than before.
Measuring the Performance of AngularJS version
Measuring the Performance of AngularJS + Vue version
Because we switched frameworks, the number and the size of resources obviously changed. The following are the number of resource requests and the sizes.
AngularJS | AngularJS + Vue | Note | |
---|---|---|---|
JS Size | 11.7 MB | 10.9 MB | |
Number of JS Resources | 29 | 32 | |
CSS Size | 2.2 MB | 3.0 MB | |
Number of CSS Resources | 10 | 12 | |
XHR Response Size | 6.9 MB | 5.6 MB | Due to an increase of 1 to the API call, file size increased by 1.2KB (Since the devtools consider JS and CSS downloads from service workers as XHR, 1.2KB increase in API standards.) |
Number of XHR Requests | 38 | 37 | Actual Increase of 1 compared to the original |
While I still do not fully understand the decrease in JavaScript file size and since this release included other fixes aside from the framework migration, the data may not be entirely meaningful. As for CSS, the use of element-ui increased the file size as expected, but the number and size of XHR requests, as I especially wanted to control, increased only by one as I intended. I was extremely satisfied that, compared to the original, there was no unnecessary XHR.
It takes enormous efforts to migrate an entire service. This article mainly focuses on not only the implementation, but the preparation stages as well. It is absolutely critical that you figure out each and every feature of the service so that there is nothing left behind. From the perspective of the users, if a feature that used to work before suddenly stops working, it can only be extremely uncomfortable. Also, because no new features were added, the users would most likely not even realize that you migrated to another framework! Finally, since we decided to include the Vue inside of the AngularJS, it is important to consider the resulting performance as well. Here are my final thoughts on the general flow of migration.
Prior research of features
I hope this article can be of help.
P.S. If you are interested in taking a look at a service with AngularJS coexisting with Vue, try the Dooray!’s Contacts, Project, and Kanvan pages. Free Trial Link