The Pain of Upgrading to an Outdated Angular version 6.png

The Pain of Upgrading to an Outdated Angular version 6

There are moments in аlmost every developer’s work-life when upgrading is considered. Either by the developer itself or the client, the vendor and so on. Upgrading can be applied to different kinds of software aspects. One of those aspects can be the framework, used for building an application, which is not necessarily the easiest to handle.

The question then arises – “Why does someone need to consider upgrading at all?”

In this article, using the Angular framework as a guinea pig we will provide examples and share our experience to help you answer that question.

The Pain of Upgrading to an Outdated Angular version 6 2.png

In general, when you upgrade a front-end framework you usually need a good plan, which often includes the following:

  • the framework used for serving the application;
  • availability of the next major and minor versions;
  • language syntax that is currently used;
  • packages (both framework-related and third-party) that are currently referenced;
  • the current state of the code (with a focus on cleaning up the unused portions);
  • the potential inclusion of compatibility packages (if any).

Which framework releases major versions, twice a year? Angular is one of those frameworks that some front-end developers consider not worth trying at all, as it might lead to constantly interrupting the overall development process by scheduling significant time only for upgrading. Even if the differences between adjacent versions are minor, sufficient time slots for upgrading will still need to be aligned in case of a large project.

Let’s imagine having an Angular project with 3 shared modules and 15 children modules (some of them tightly coupled), with hundreds of components, maintained by 4 different teams (not necessarily distinctive by module). On top of that, the application uses a significant number of external packages for different purposes that can have dependency issues and the preparation for scheduling a time slot, just for an upgrade, has a significant impact on the teams’ planning, sprints, roadmaps and so on. Needless to say that without a long and proper analysis, nobody can determine how long the upgrade period can take.

And it is not just the planning (some teams can be working on more prioritized topics) and the development effort (some aspects of the upgrade might require more than a straight-forward refactoring), the changes as a result of the upgrade outcome will need to be later verified which usually “translates” into testing the entire product.

Why upgrade to the “non-latest” version?

In the intro, we asked a more general question – “ Why does someone need to consider upgrading at all? ”

Let’s begin with three different examples for the same application: one that uses the latest version of the framework and another two that are one and three versions behind it, respectively. The best possible outcome that favors the upgrade is that the latest version is stable and more benefits can be exploited. It means that the time invested into upgrading one more time than the second application and three times more than the third application, paid off.

Hence the third application is in a state where it needs to either directly perform a risky direct upgrade or to schedule three subsequent upgrades without much time in between to catch up with the first application and make use of the latest version’s advantages.

In the worst-case scenario, the latest version’s benefits (if there are any) are not suitable for the current development at all, nor for the maintenance, and the team working on the first application since they lost some time in planning and performing the upgrading to achieve nothing more than incrementing a number. Not to mention that the stability of the latest version can also be put into question.

Do you believe you already have an answer? Well, it’s not that obvious, therefore let’s dig deeper.

In comparison to other frameworks, Angular also states that with every new release, the new changes that come with it, can in fact optimize your current application and make the upcoming development easier. However, we will not provide any details on those improvements since it’s way beyond the scope of this subject.

The majority of Angular experts and contributors advise the following upgrade pattern – constantly upgrade to the latest stable version. It means that every time a major version is released, you are encouraged to upgrade to that version before the next major release. Hence if your current version is several “numbers” behind and you decide to upgrade to the latest stable version, they recommend doing it step by step, which means performing several upgrades in order to “reach” the top version.

The Pain of Upgrading to an Outdated Angular version 6 3.png

The bottom line is that by upgrading to just one version above, you:

  • avoid more code refactoring to adjust to the new version;
  • deal with the possible package dependencies more easily;
  • take advantage of new ways of upgrading to the next versions.

What approach did we choose?

Our project started in version 2. Since then we performed one major upgrade, directly to version 5. Following that, because of tight schedules, roadmaps, and other priorities, we were not able to find sufficient time and resources to upgrade. When we finally decided to upgrade, the latest official stable was version 8.

During the planning meetings, there was an option to move directly to v8, but a large amount of code using the old @angular/http package convinced us that by the time we finish with refactoring, we can at least migrate to v6 and take advantage of any new options that can ease the next upgrade. Therefore the path to reaching our goal before v9 is released looks like this: v5 -> v6 -> v7 -> v8. It meant that three different subsequent upgrades will have to be performed in total, supposedly shorter than performing a direct upgrade.

Since we already mentioned the HttpClient package, it stayed deprecated for several major Angular versions. However, it is always important to at least keep an eye on the deprecations in your project if you can’t deal with them on time. In fact, you do not want to wait for the obsolete stuff to be completely removed from some future version you wish to upgrade to!

Keys to upgrade to v6

Why is it worth explaining the key details of this specific upgrade from v5 to v6?

Besides the issues that you might have to deal with and spend hours investigating, v6 introduces a few groundbreaking changes such as:

  • different format of the configuration file;
  • ng update CLI command;
  • RxJS 6.

There is a lack of precise documentation to upgrade, especially to v6. This happens mostly because those versions were the latest at the time and the commands to upgrade angular/cli stayed the same. So when someone seeks a guide to upgrade to a specific version that is not the latest, it ends up upgrading to the latest official version instead of the desired one.

We started with the official recommendation from Angular and soon learned that it was far from enough. Although the upgrade guide has three different options for the application complexity, probably there is not enough investigation done on a significant pool of large and complicated projects and how the upgrade process behaves in those conditions.

The initial step was very wrong – instead of upgrading to the base version 6.0.0, we tried to migrate directly to the v6-lts i.e. the latest minor version which was 6.2.9. By doing so, you end up with a lot more package dependency issues than expected that in most cases will have to be dealt with one by one.

We had to start upgrading all over again. Therefore our advice is to upgrade to the first major version i.e. not directly upgrade to one of the minor Angular versions unless you are trying to switch majors.

For example:

  • 5.0.0 -> 5.0.6 – ok;
  • 5.0.6 -> 6.0.0 – ok;
  • 5.0.6 -> 6.1.1 – not ok.

After we finally succeeded in upgrading one environment, we discovered that there are additional issues to encounter when performing the upgrade in other environments. We gathered all those issues with their resolutions and then generated one procedure that applied to the project elsewhere.

Node j.s

Since Angular in most cases uses node.js to take advantage of its package manager npm, the first thing to check is the version you are currently using – in our case 7.6.0. The official recommendation of node.js for Angular v6 is to use at least 8.0.0 as a minimum requirement. At the time of the upgrade, the latest stable node version was 12.7.0, and we decided to use it. Surprisingly this approach turned out to be a failure, because of the following two reasons:

The Pain of Upgrading to an Outdated Angular version 6 4.png

First, some packages require a specific range of node versions. One such package is the sass bindings provider node-sass package. Not resolving this issue can lead to CSS inconsistencies, such as not being able to recognize the Grid layout module or reference the LibSass library at all.

Second, because of the fact that there are different ways of installing/upgrading node.js, you can end up having multiple versions at once in your system. And even though you can use only one at a time, the other node versions are still in an active state and occasionally they will interfere with the setup and running of the application. In some environments, there can even be a parallel version running without you even being aware of it.

To avoid those two problems, we recommend using nvm for managing node on your local machine and especially use the deactivate option so that you can be sure there are no other versions of node besides the one you actually want to use for Angular purposes.

But first, you need to locate the right version. Unfortunately, there is no easy way of automatically doing that, except checking one version at a time, starting from the minimum required version that is officially announced. In our case, we started with 8.0.0 and finished with 8.9.0 while trying out most of the versions. Even though at the time of the upgrade there were official 10.x versions of node.js available and recommended for usage, regardless of the purpose, our advice is to use the minimum required version to avoid other compatibility problems.

TypeScript

Almost the same thing happens with the TypeScript version. The official recommendation for minimal requirement stated 2.7 but in fact, the lowest version that did not cause any problems was 2.7.2. And again, it was related to package dependency issues.

One odd thing you need to consider is that even though your noImplicitAny option is set to false in your tsconfig.json, you may still encounter compile errors on undeclared variables. You can still compile and run the project but if you really want to get rid of those errors you will have to set the declaration of those vars to any.

JQuery

JQuery package can often be a part of your project, especially if you include bootstrap controls, which leads to one more reason for dependency issues. But don’t be afraid, in this case, at least you can easily track those issues because they are almost entirely connected to the bootstrap packages.

Here, we would only suggest using the lowest possible version of the jQuery package, unless the dependent packages disallow it. The latest jQuery version at the time of the upgrade was 3.4.1, but we stuck to 2.2.0 in our package.json.

RxJS, but with compat

Version 6 of RxJS package comes with changes that will force you to make a lot of code refactoring that is mainly caused by different import statements, methods, operators, and so on. So errors, related to the reference changes of observable, promise, zip and other “reactives”, are very likely to pop up during the compile-time:

ERROR in node_modules/rxjs/internal/types.d.ts(81,44): error TS1005: ‘;’ expected.

What if we tell you that by just installing only one more package you can regain the backward compatibility? I guess you will agree that this suggestion is preferable over changing hundreds of lines of code. Version 6 of the rxjs-compat package needs to be installed along with RxJS v6 and you will preserve every line of code. This is a really good example of how the inclusion of new compatibility packages in the project does not bring much code refactoring (in our case it actually prevented it) and does not bring additional complexity.

Whitespaces are gone

At this point, your Angular 6 application is compiling without problems and you can run it to verify that nothing is broken. You open the first pages and something seems different than before. Some of the html elements are glued next to each other, like there is no spacing left between them at all. The first thought that comes to your mind is that probably some css related package issue occurred again.

“angularCompilerOptions”: {
    “preserveWhitespaces” : true
}

Luckily this is related to the change in the default Angular compiler option for preserving the white spaces. Just set the preserveWhitespaces property to true in the angularCompilerOptions of tsconfig.json file and the pages will be rendered back as before.

Initial and subsequent upgrades

The environment on which the upgrade is initially performed will require more investigation and a larger set of commands to be executed. A specific list of commands needs to be executed on the existing project and as a result of the upgrade there will be some changes, mostly on the configuration files like angular.json and package.json. Here is the sequence that should in general work, assuming that you have set up node.js properly:

npm uninstall -g @angular/cli
rm -rf node_modules
npm cache clean //if required use –force

//at this point you can remove package-lock.json from your project, if exists
npm install -g @angular/cli@6.0.0

npm install @angular/cli@6.0.0

//at this point the version of your Angular related packages should be changed to 6.0.0, and remove the carets if any
npm install

npm install rxjs@6.0.0 rxjs-compat@6.0.0 –save //optional, execute only if you encounter rxjs issues as described in the previous section

If for any reason you detect vulnerabilities, we recommend dealing with them one by one by carefully installing the required packages or incrementing the versions of the existing ones. You can use the npm audit command for reporting the suggested interventions and solve those problems manually (yes it will probably take some time, however you can easily pinpoint the problems), but please by all means do not immediately use npm audit fix as it might try to deal with the vulnerabilities at once.

If successful, the procedure should be applied to other similar or different environments and is expected to have less manual interventions besides installing the packages from an already altered package.json file. The following command sequence should be sufficient:

npm uninstall -g @angular/cli
rm -rf node_modules

npm cache clean //if required use –force

//at this point you can remove package-lock.json from your project, if exists
npm install -g @angular/cli@6.0.0

//package.json already has the required changes
npm install

Upgrade consequences

Let’s avoid describing the new opportunities that v6 brings after the upgrade and instead just mention some consequences that you must be aware of, mainly because of the change of the format of the angular.json file. Serving the application under different non-default environments now requires setting the configuration argument instead of the previous env as well as the target argument which is unnecessary and is specified through the environment. The transition looks like this:

ng serve –env={your_env} -> ng serve –configuration={your_env}

or

ng serve –env={your_env} -> ng serve -c {your_env}

What has changed or added in the package.json after the upgrade besides the Angular-related packages? If you properly deal with the vulnerabilities, you will not have to change that much, not even the suggested zone.js package. Here is our list of changes:

Added
“@angular/platform-server”: “6.0.0”
“rxjs-compat”: “6.0.0”
“@angular-devkit/build-angular”: “~0.6.0”

Changed

“@angular/animations”: “6.0.0”,
“@angular/common”: “6.0.0”,
“@angular/compiler”: “6.0.0”,
“@angular/core”: “6.0.0”,
“@angular/forms”: “6.0.0”,
“@angular/http”: “6.0.0”,
“@angular/platform-browser”: “6.0.0”,
“@angular/platform-browser-dynamic”: “6.0.0”,
“@angular/router”: “6.0.0”
“@angular/cli”: “6.0.0”,
“@angular/compiler-cli”: “6.0.0”
“typescript”: “2.7.2”

Another good thing is that the upgrade does not affect the protractor and karma packages nor does it change its configurations which means that you don’t need to take any action related to your unit tests or e2e tests.

Conclusion

After carefully reading our experience, you can easily come to the conclusion that in general, upgrading Angular to any version is a project-specific problem that requires careful planning and serious consideration of some key points that were explained in the previous sections.

What did we learn from this experience? We learned that we will try to prioritize the upgrade process each time a new major release pops up and share every solution to a tricky problem that we may encounter during the process.

We are sure that if there is still someone out there in the universe that for whatever reason still wants to upgrade to this outdated version, the strategy we described in this article will definitely come in handy.

Are you an Angular Developer who is looking for a company where you can build a dynamic career in your field of interest, work with professionals to gain industry insight and enjoy ongoing learning and development opportunities? If this sounds like you, then make sure you check out our current job openings.

Velimir Graorkoski

Jun 23, 2020

Category

Article

Technologies

AngularJavaScript

Let's talk.

Name
Email
Phone
By submitting this, you agree to our Terms and Conditions & Privacy Policy