Running AngularJS 1.6 in Angular 5 (side by side) Part 3: Build, deploy and beyond

Release it!

This is a part 3 of Running AngularJS 1.6 in Angular 5 (side by side), you can read part 1 and part 2.

We’ve completed the new features and it’s now time to release to the production.

Today, we’d like to share our experience with the release process including

  • Building a release bundle
  • Deploy to S3 bucket
  • Lessons learned

Building a release bundle

AngularJS

In AugularJS, we used Grunt (generated by Yeomen) to create a release bundle. Grunt build performs:

  • Build a css file from scss
  • Convert html templates into a single js file
  • Bundle js files including the libraries into a single js file
  • Uglify js and css
  • Revisionize the file names
  • Update/minify index.html
Gruntfile.js build task

The release bundle contains a set of js/css/image/font files with index.html.

The dist directory contents

Angular

In Angular, we use Angular CLI, which uses Webpack internally. There are 3 main steps for building a release bundle:

  1. build with new angular cli
  2. minify/uglify with a generic minifier/uglifier
  3. Revisionize (Force loading the new release)

The reason we need to minify/uglify by ourselves is our AngularJS app does not work when using Angular CLI uglify process! In another words, Angular CLI build is not compatible with @angular/upgrade we use to run AngularJS side by side. It is a big surprise given Angular team is providing comprehensive tools and documentation for the upgrade module. There are many issues Angular CLI repository complaining about it.

Build with Angular CLI 1.7.4

We need to firstly make sure AngularJS app is uglify friendly for a generic minifier/uglifier by converting any implicit dependency injection to explicit dependency injection:

someModule.controller('MyController', function($scope, greeter) {
// ...
});

to

someModule.controller('MyController', ['$scope','greeter', function($scope, greeter) {
// ...
}]);

When AngularJS injection has been fixed, we can run ng build (Please see the official doc for options).

ng build --aot true --build-optimizer true --sourcemaps false --extract-css true --named-chunks false

The following is the contents of dist directory. There are four JS bundle files including inline.bundle.js, main.bundle.js, polyfills.bundle.js and scripts.bundle.js and one CSS bundle file, styles.bundle.css. At this stage, JS files are so large, a total of 20MB.

It’s also worth to mention that ng build does not bundle the partial html files as scripts unlike AngularJs Grunt ngTemplate task. This will force us to copy ng1 directory to serve partial html files at the time of page loading.

Minify/Uglify

We have used ‘uglify-js’ version 3.4.2 and ‘uglifycss’ version 0.0.29:

echo "run js uglifier"
node --stack_size=100000 uglifyjs ./dist/inline.bundle.js > ./dist/inline.01.bundle.js
node --stack_size=100000 uglifyjs ./dist/polyfills.bundle.js > ./dist/polyfills.01.bundle.js
node --stack_size=100000 uglifyjs ./dist/scripts.bundle.js > ./dist/scripts.01.bundle.js
node --stack_size=100000 uglifyjs ./dist/main.bundle.js > ./dist/main.01.bundle.js
echo "run css uglifier"
node uglifycss ./dist/styles.bundle.css > ./dist/styles.01.bundle.css

The process reduced the file sizes to almost half, which is still over 10MB. This is 4 times larger than the previous AngularJS app (2.5MB including all partial html files).

Revisionize (Force loading the new release)

Lastly, we need to revisionize the files in order to force reloading the application bundles. There are a range of tools available for a rev task including grunt and webpack. (we used a custom rev task which is already existed in our tool chain).

Deploy to S3

DataFarming is hosted in AWS S3 as a static content and served using AWS CloudFront. We use AWS CLI to sync the dist directory with the S3 bucket and then invalidate the CloudFront cache. There is no change in this part and we now have a new release!

A new feature released in production

Lessons learned

Finally we’d like to share our lessons learned on this migration strategy.

Good parts

  • No Glitches: Overall, the migration is smooth and there is no technical issues in building a new feature using Angular while leveraging the existing features implemented in AngularJS
  • Brave new world: We can now fully take advantages of Angular Component Architecture conjunction with NGRX as well as use UI components available in Angular Material
  • Align with Angular eco system: Angular is now 7 and providing more features that help us improve our application development/features (Schematics, Univeersal, Angular CDK etc.)

Bad parts

  • Build issues: ng build does not understand that we’re including AngularJS using the upgrade module and there is no discussion or clear guide for how you can solve it. The Angular CLI team also clearly says they don’t want us to touch any of the webpack tasks directly. So the issue we face had to be solved by ourselves
  • The size of application: The application has become significantly larger compare to the previous release (4 times larger than AngularJS app). We’ve built a number of applications using Angular 2, 4, 5, 6 and 7 and the size of a release bundle can be 500KB ~ 5MB depending on the libraries used for a project. We also experienced earlier versions of ng build with eager optmizations often broke the app for no reason and we were forced to release (quietly) a version without optimising the release bundle.
  • Benefit for the end users: As a developer, this project is successful in a way we can leverage the newer technology for the app, it is not beneficial for the end users at all due to the increase in size. This is especially a problem for people who access our application in a field with a mobile device and they could easily stop using it because of the loading time.

Do we use this architecture for another AngularJS app?

The biggest motivation of this migration strategy is to add new features using the latest technologies while reusing the custom implementation (services, directives and all the reusable parts) of the AngularJS app.

DataFarming has definitely achieved the goal and substantially improved a new feature development from a development perspective. It is also economical as we are able to use all of the existing implementation.

So we’ve decided to try on another larger project.

Case: Angular UI Bootstrap based project

Many of our AngularJS apps use AngularJS UI Bootstrap (which uses Bootstrap 3). The issue we faced quickly was Angular powered Bootstrap (UI Bootstrap for Angular 2 ~ 7) use Bootstrap 4 and you can’t simply use these 2 versions of Bootstrap side by side. The end of the story.

Our solution for the UI Bootstrap based apps are to run 2 single apps (AngularJS and Angular) side by side by having 2 application entries (i.e. index-1.html and index-2.html). Any services/directives implemented in AngularJS required in Angular are simply ported (duplicated) in Angular by rewriting (mostly copied and pasted with a small modification) in TypeScript. The routing is simply loading index-1.html or index-2.html depending on the route.

Conclusion

We’re initially very excited with the upgrade module provided by the Angular team. They provided a comprehensive in depth technical document on this topic, however it was not practical and it did not help us to make what strategy we should take in terms of the user experience.

In our experience, the development process has been great and we’ve built our new feature quickly and nicely using Angular echo system. However it is not really friendly as a end user as the application is significantly larger to load.

We can certainly spend more time to further optimise but it is not that straight forward since we’re not be able to ‘customise’ Angular CLI build operation and there is no guarantee if a custom optimisation would work in the future version such as Angular 6 and 7. For instance, Angular 6 uses angular.json instead of .angular-cli.json and it is radically different structure and you need to deal with all internal process changes yourself.

This is definitely a major drawback of using Angular CLI centric development compared to using a traditional webpack as you cannot hack your build process easily for a special requirement (e.g. embedding AngularJS app or dealing with another embedded libraries).

In conclusion, if you don’t have too many 3rd party libraries in AngularJS app and the size of your app is not important this strategy will work for you. If you use UI Bootstrap, 2 single page apps side by side is the way to go.