 Hi everyone. Today I'll be talking about moving from legacy code base to a modern frontend. I hope you enjoyed this story. It is kind of a work in progress, so it's not really finished yet. Like my team hasn't completed the migration, so the presentation will be just cut in certain point. But I think there's still a lot of things to talk about. So before I start, my name is Gregorz. People called me Greg. I'm head of engineering at Caligo Solutions. We build software for banks and airlines. And I've been using JavaScript for almost 10 years, but it's always been kind of a second or third language for me. So I primarily use Ruby and Elixir, but because I've always been developing for a web, I had to use either like, you know, the old, very old JavaScript like jQuery, or the more modern one like Node.js or TypeScript. So the product that I will be talking about is called Travel Edge. And it's a travel booking site. So we offer hotel bookings, car bookings, and flight bookings. And we developed it as a white label. So we basically tell banks that, hey, if you want to allow your customers to use their credit card points to spend them on hotels or flights, then we can give you that platform. And the code base has been developed since 2014. And even though it's relatively new, it's just for years. I believe that in JavaScript world, it's like a new era because it's been developed before really the modern features took off. And now it's a multi-tenant application. So we can deploy this application separately for each tenant, like you know, you separate database, separate servers, et cetera. But we can also use a shared hosting so we can determine the tenant based on the request host and some other attributes. So each of the tenants has just some custom JS code. Like we use shared JavaScript code and we use some custom code on top of that. And we can customize like the whole front end. Okay, so what do we start with? Our technology stack when we started was Ruby on Rails for the backend. Then for the deployment, we use a platform called Opsworks. And even though it's not related, it is quite important part in this talk. Then we use Prokets, which is part of Ruby on Rails asset pipeline, which is basically a way to get from any kind of source code like JavaScript or, sorry, like TypeScript, CoffeeScript, et cetera, to bundle together in one file for the browser. Then we use Bower. I'm not sure if you remember that. It still exists. It's in maintenance mode. We used CoffeeScript and we used AngularJS2. Now what we want to end up with is we don't want to change our backend, but we basically want to change everything else. So instead of Opsworks, we want to have Docker containers. I will skip this part during this talk. Then we want to move from Prokets to Webpack, from Bower to Yarn. We want to rewrite our CoffeeScript to TypeScript and move to the modern version of Angular. So in my presentation, I will focus on these four parts that are most relevant for this meetup. The first part will be about CoffeeScript to TypeScript migration. How the migration looks like. Basically, I don't know if you've tried using CoffeeScript before. It's kind of like TypeScript. It's just another language that compiles to JavaScript. CoffeeScript has a syntax that is more familiar to Ruby and Python developers, and back when it was created, which was in 2010, if I remember correctly, it was quite a big thing, because it added a lot of nice features on top of JavaScript. Basically, it allowed you to write 10 lines of code, rewrite them in three lines of code. Over time, CoffeeScript became less and less relevant. So the features in ES6 and later ES2017 made it kind of deprecated. So you can still use CoffeeScript. And right now, the CoffeeScript compiler, we actually compile it to the modern JavaScript. But really, nobody uses it anymore for new projects. And that's kind of a problem for us, because finding new developers who want to work with CoffeeScript and who understand CoffeeScript becomes harder and harder. So this is one of the reasons why we migrated to TypeScript. And now, the migration process, it seems quite easy. So basically, you need to add TypeScript as your dependency, write one TypeScript file, try to compile everything, and make sure that it works. But on top of that, we need to research some good TypeScript practices. So it is kind of a new language. It adds new features, and we didn't have anyone who was very experienced in TypeScript at that point. We're all more CoffeeScript developers. So we needed to do a bit of research on what to do with TypeScript, what not to do. We didn't want to just rewrite it to another language and not to use its features. We didn't want to have, like, you know, CoffeeScript code just in TypeScript syntax. So after we did some research, we asked the most experienced developer to rewrite a few example files, like one service, one controller from our Angular code base. And after that, the others could follow. So this was like the idea that, okay, just give an example to people on how to do that. And after that, once the research was done, once the initial work was done, we started writing all new files in TypeScript. And whenever there was some work to be done in some big work to be done in some CoffeeScript file, we said, okay, we let the developers decide, okay, this work is big enough to rewrite this code in TypeScript. So this is not a perfect way to migrate because it keeps both TypeScript and CoffeeScript in your code base for a long, long time. So that's one of the challenges. So there were a few challenges during this migration. So the first was to make TypeScript and CoffeeScript work together. It's not as easy as it seems, primarily because our, the next part of this talk, the Spockens, the Ruby library made it a bit messy. And sometimes the TypeScript files and CoffeeScript files just just well and bundled together correctly. And what I talked about a minute ago, it's hard to prioritize a task. When you're a startup and you have really a lot of stuff to do and you know, I'm sure some of you at least know how it feels. It's very hard to tell the leadership of the company that's, hey, so this week we want to spend some time rewriting the files because then they ask like, okay, so what's the point of that? And we're like, well, it will be just rewriting the new language. For us developers, it makes sense because then we don't have to deal with this old language. So the new developers who come, they don't have to learn the new language. They don't have to understand that code base. But it's really hard to prioritize it. Also, one of the initial challenges was that a CoffeeScript is dark type. So it doesn't have any type annotations like JavaScript. And because of that, we didn't have experience with using types. And there were basically no type annotations anywhere in our frontend code base. And it's hard to initially start using types because you can start using types in one file, but then majority of your code ends up with the type called any, which means basically any type, right? Because how can I use another, how can I use specific type if all of my application just doesn't define types? So it's basically impossible. So initially, we just had to add a lot of type definitions, a lot of interfaces, etc. before we could actually start using them in the whole code base. So the result of that part is that for now CoffeeScript and TypeScript still exist together in our code base. We are on a steady path. So we are migrating every new piece of code is written TypeScript. CoffeeScript is just for minor changes. Some files in CoffeeScript will remain there for a long time just because these files are not really touched by anyone. So if something is not used, it hasn't been modified for like two years, then we will just let it stay there for a while. It's not a big problem. But we are on a path to be 100% CoffeeScript free in a couple of months. And we're very happy about the new features that TypeScript provides. So people are often resistant to use statically typed languages, especially if you work with languages like Ruby or Python, then you have the whole power of the dyke typing and you just don't have to worry about types. But eventually you find that they are useful, not everywhere, not always. And TypeScript gives you this good place where you can use types, but you have the any type. So if you don't want to use types, you can just ignore them for a while. So that's the first part of our migration. CoffeeScript to TypeScript. Now, as a relation, the second part is Bower to Yarn. So first, why did we start with Bower? Bower years ago used to be a go to solution for JavaScript files management, especially for the front end. So before Node.js really took off and people started using the NPMJS for all the packages, Bower was kind of a thing that allowed you with minimal configuration to download the JavaScript libraries and configure like manage them in a very, very easy way. However, because the NPM repository is so much bigger, Bower became kind of irrelevant because people figure out, okay, if Bower is only for the web and NPM is for everything, then why don't we use everything in NPM? And then Bower just became kind of deprecated. Even right now on the main page, they say that the Bower is maintained, however, they recommend switching to NPM or Yarn or some other package manager. Then we had plenty of issues with Bower during deployments. I think Bower is hosted on Heroku and I don't know what's the configuration there. I believe that Heroku just sponsors servers for that. But sometimes we had troubles. There were lots of timeouts during deployments and that caused us like, you know, it gives you a lot of stress if you're deploying a few servers and one server or two servers just do not deploy correctly because of the timeouts. So at some point we just decided that here we can't do it anymore. This is too unreliable. And on top of that, it was harder and harder to find certain packages in the Bower. Like the JavaScript library maintainers, they just don't add the Bower.json file anymore. They just put everything on NPM. So that's why we decided to switch to Yarn. So at that point of time, Yarn was this new hot thing and everyone started recommending that. And I'm not sure right now, but I think the NPM kind of caught up right now. So the difference between them is not so big anymore. But back then, all the comparisons said that, hey, Yarn is much better and it's just like a drop in replacement for NPM. So we decided to do that. And Yarn was also recommended by Ruby on Rails core team. So they were working on a seamless integration for Rails with Webpack and they just said that by default, this library will use Yarn. So that's one of the reasons why we decided to go with it. Now, the migration path here was first to make sure that Yarn is installed on the server. So like, you know, just add some script to the server setup. Then we had to convert the Bower.json file to the package.json. This was very easy. Replace packages that are not present on NPM. This proved to be a bit more challenging. I will talk about it in a moment. And then we had to update asset paths. So this, again, Yarn, sorry, Bower was installing things to Bower modules. NPM is installing to Node modules. So it just had to make sure that the assets are available everywhere. So there were two challenges with that. First, our deployment solution only used some really ancient version of Node.js. If I remember correctly, it started with zero, which, yeah, I think you know how old it is. So yeah, so the Officeworks platform, which is part of a AWS platform, it's kind of like a web interface over Chef. So it had some pre-installed packages and some Chef recipes that were just built in. But all of them were very old. And even though you can recommend and ask them on GitHub to add the support for new versions, all of the issues are basically ignored. So the only way to deal with that was to write our own Chef packages and do some really hacky stuff on the Officeworks platform to make it work. So eventually, we did that. But it wasn't a pleasant experience. Like, you know, when you go to your platform and you see that the last version of one of the most critical libraries comes from five years ago or even before, that doesn't give you a lot of confidence about the deployment platform that you're using. The second problem is that lots of people, when they were moving packages from our repository to NPM, they realized that the name of the package was already taken by someone else who needed that package to be published on NPM before or the other way around. So we had certain packages that we just couldn't find them on NPM. Like, the package was there, but under the original name, there was something else. So we had to find the equivalents. And it wasn't very difficult, but it was just, you know, kind of tiring and took us some time. But eventually, the migration was very successful. So we are now 100% working with yarn. We forgot about the borrower entirely. And there is no more issues with the deployment. So the NPM repository doesn't cause us any timeouts. We haven't had any problems with that. Now a bit more challenging thing. So we were moving from Sprockets to Webpack. And I'm not sure if you ever work with Sprockets, but back when it was released in 2011, it was really a big thing. So before that, bundling JavaScript files was basically, nobody did that. Everyone, whenever you wanted to add JavaScript file to your application, you just added the src tag in your HTML. And when the user opened the page, they just downloaded 20 or 30 JavaScript files. So Sprockets, in 2011, they introduced this concept. I'm not sure if they were first, but I believe they learned it popular that they started bundling everything into one file. They added the fingerprint at the end of the file. So before that fingerprinting, if you change your file, lots of users still could see the old one because of the browser caching. Because nobody figured out how exactly to cache the files. Because if you want to deploy the file any time, then you cannot give any cache. But then you want people to cache that file because you don't want them to download the same files over and over. So that caching was kind of a problem 10 years ago. But with the fingerprints in the name of the files, the problem was gone. So Sprockets were quite a fun thing to work with. And there was minimum configuration required, especially if you work with Ruby on Rails. It's just, you know, it's kind of built in. However, again, over time, like with Bower and with CoffeeScript, it kind of became less and less relevant. So Sprockets have its own kind of own DSL. They use their own directives like require, or directory, etc. They do not support the common JS or system JS. So there is lack of support for the modern JavaScript modules. But to be fair, that's it. I think that Sprockets is actually still pretty good. And if you don't want to deal with the webpack or branch configuration, then this is still a good thing to use right now. And here a small digression. So I remember during my time working with JavaScript, there's been really a lot of bundlers like Grant, Galb, Branch, and a couple of more. But whenever I read some blog post of someone saying, this is how we migrated from Sprockets to Galb or Grant, then they say that, okay, so we added this one file that was 100 or 150 lines of code. And that's how it works. And like, okay, so the cost is writing 150 lines of code. What is the reward? And the reward is like we are on the modern platform right now. And as you can imagine, none of this platform is modern anymore, right? So the reward is gone. Yeah, so the cost was always too high for me to migrate. And I think that also shows the power of built-in support. So with Ruby on Rails, I never had to configure Sprockets. So I could move from zero lines configuration to 100 plus lines of configuration. And it was just, I was never motivated enough, just because I wanted to be a more on a more modern platform. However, eventually, we decided to move to Webpack, because it supports the modern modules for JavaScript. It's kind of a mature library. It's well tested solution. And now Ruby on Rails provides built-in support for that. So again, we don't have to write the Webpack config from scratch. And we just use a kind of a default config that Rails provides. And we just customize a couple of options. So we chose Webpack. And we never really considered other options. I don't know what are they, I don't even know what are the alternatives right now. Every once in a while, I see the blog post that says like, oh, now Webpack is so powerful that you have to write 100 lines of code, use parcel. And then people say, now parcel is becoming too powerful. So we just decided to go with Webpack because we knew that it's so popular and so big. And it was a very new library back then. It was in version 3.0 something. We said, okay, let's go with it and just configure it once and hope that we don't have to work with that. We don't have to come back to it all over again. Now the migration was a bit complicated. Because Sprockets has its custom directives, it was a big step for us to move directly from Sprockets to Webpack. So what we did initially was we moved from Sprockets to Browserify. So Browserify, I believe it's another bundler for JavaScript that just supports the common JS. So it supports the require function. So this way, using some library that allows us to mix both Sprockets and Browserify, we could move step by step. So I could play a bit with Browserify. I could just install one or two libraries and use it via Browserify and still keep Sprockets. And it worked. It deployed fine. And it didn't cause us any problems. So this allows us to experiment a little bit and check how everything works. And then step by step, we moved fully to Browserify. And then from there on, we moved to Webpack. Now, there were really a lot of challenges here. The biggest one was the memory usage. I don't know how much time I spent trying to figure out why Webpack needs that much memory. Like, I was compiling the same JavaScript files that I was compiling with Sprockets. And Sprockets never crashed. But Webpack really gave me a lot of problems. So finally, I found out that I need to use the node options with, like, max old size or something like this, which basically gives Node.js more memory to use. I think by default, the limit is one gigabyte. And we just increase it to four gigabytes. And that prevented the crashing. I still don't understand why compiling a bunch of JavaScript files that all together are like two megabytes or maybe five megabytes, why does it require more than one gigabyte? I have absolutely no idea. But increasing memory solved my problem. So I don't need to have this idea anymore. And for that, I'm blessed. Now, the next challenge was the deployment speed. So when I finally figured out that, hey, if I increase the memory to four gigabytes, then it works. Then I realized that also the deployment went from 10 minutes to 30 minutes. And again, I had no idea why. Why my deployment became so slow? Like, I'm using technology that is supposed to be faster and newer and better, and it gives me all these problems. So as it appeared, it was a memory problem again. Because even though we set the limit of memory to four gigabytes, our server didn't have that much free memory. So it had like 10, two gigabytes, and then it moved to swap. So of course, it's much slower than using RAM. And that's why the deployment became so slower. So what we had to do is to make sure that the servers have more free memory. This is a bit of a problem, though, because with our current deployment solution, the same server compiles the assets and runs the application server. So if we had switched to something like Docker before, we could have just one very big server with lots of memory that would compile everything and just push the Docker container, and then the application servers would fetch the containers and swap them. But with Chef, the deployment is that you basically connect to the server, you fetch the source code, you compile the source code, and you run it. And it is kind of a waste of resources because the server could use all the memory that it has. But if it uses all the memory, then there is nothing left for compilation. So this is one of the reasons why we are migrating from our current deployment solution. Eventually, we just increase the memory on the servers. So now the deployment is back to 10 minutes. But we know that if we use even better servers, then we could just make it even faster to five minutes or so. And then another challenge was with asset fingerprints. So this is something that we didn't detect in staging. This is something that we detected only after deploying to production, which we had to immediately roll back. Now what happens is that because we compile assets on each server, and by default, the fingerprints of the compiled files depend on a timestamp. It means that each server ends up having files with different timestamps. So let's say that I have five servers, and I download the index.html file from server number one. And it has, in the HTML, I have references to certain JavaScript files. And now I try to fetch these JavaScript files, but instead I connect to servers 2, 3, and 4. And the files there have different fingerprints. So the file that I want is not there. So what happens is that the files are basically not there. Now we immediately rolled back. So the issue was there just for like 10 or 50 seconds. But it gave us a lot of stress. Like, you know, why does it happen? Of course, the fact that the fingerprints depend on timestamp wasn't obvious to us at that point. It's something that took us a while to realize what's going on. So the solution for that was a plug-in for a pack that just generates the fingerprint based on the content of the file, not on the timestamp. So I think what happens there is that it just takes the whole file and it calculates the MD hash of that file. But again, this is something that it gave us a lot of stress. It was quite a big challenge. And it was something that is very difficult to detect, especially when you just do it on one staging server. And then the last thing that was quite problematic was local compilation speed. Because our application is a multi-tenant solution, and each has its own JavaScript files. And locally, each file takes, I don't know, like five seconds to compile. Then if you have 10 tenants, then it means that each compilation takes like 50 seconds. If you have 50 tenants, then it means that each compilation, and you know, if you use something like a Webpack Dev Server that just recompiles things after every file save, you can't just wait five minutes for the refresh. So what we did is we just, for the development environment, we have just configuration file, which we, basically what we say is that if I work with a certain tenant right now in my development environment, this is the only tenant that will have its files refreshed. It's a JavaScript file refreshed. So that helps us to solve the problem. And now as a result, of course, all the JS files now are processed by Webpack. CSS, we still use sprockets for that. We are considering migration. I don't know exactly what are the benefits of that, but we are still, we are considering it. The deployment time for now is comparable, but eventually once we move to containers, and we have a very big server for compilation, for builds, it should be much faster. Development is faster. It's much nicer to work with Webpack than to work with sprockets. And because of the integration between Rails and Webpack, we can still use the helpers in our Rails templates. And now the last, the biggest, and the most painful migration that I will talk about today. First, let me explain why we had AngularJS. So at that point of time, remember, this was early 2014, Angular was kind of a thing to use in the bigger applications. I think React was just half a year old back then, and it was still not very mature. Backbone, on the other hand, was already old and too mature for that. So AngularJ was kind of a thing to use. There were a lot of learning resources that was rich ecosystem, that the framework was growing, the developers, they were very active, and it made it easy to develop single-page applications. However, over time, like with everything, AngularJS became kind of a legacy thing and kind of a problem for us. So the AngularJS framework was replaced by Angular. Now, the names are confusing, so later I will be referring to them with their versions. Basically, AngularJS is version 1.x, and then the whole framework was rewritten in TypeScript, they removed like 80% of the concepts, they left some similar stuff, and AngularJS is right now in maintenance mode. So the last version I think is 1.7, there will be no more features added, there will be only security issues will be solved. The ecosystem is shrinking, so nobody now develops new libraries for AngularJS because the framework is in maintenance mode, right? So it doesn't make sense. And the code was very coupled there. So I don't know if it was specific to our application and we didn't have just very good practices, but I think that AngularJS doesn't promote loose coupling. So it gives you some powerful things like root scope, which is basically a global variable which is available everywhere in your controllers, in your templates, in your services, everywhere. And you know, if you don't know where to put something and how to synchronize different places of your code, then you just put this stuff to root scope and it works. Of course, it's crappy because it's just one huge global container, but hey, it works. So when we decided to drop AngularJS, we had multiple options. And eventually we realized that we will just migrate to Angular, which is like a successor of AngularJS. Why? First, because it was the most natural migration. So even though lots of concepts were removed, still we can reuse a lot of code. And our team was already experiencing Angular, so it was easier for us to migrate to the newer version than to learn entirely new framework. And at least in theory, there was a very clear iterative upgrade path, which is called ng-upgrade. So Angular core developers build this thing called ng-upgrade that allows you to run both AngularJS and Angular applications together. And then the ng-upgrade allows you to synchronize the state of these applications. So in theory, it's awesome in practice a bit less, but at that time that was something that we say, OK, this seems like the easiest way to migrate from this old framework to the new one without stopping the development of new features for a long time. So the migration path was, first we needed to upgrade our code to AngularJS 1.5 because it introduced some things like, for example, components. Components are critical things in Angular6. This is the latest version that we're migrating to. And before Angular 1.5, this concept is there. So first we had to upgrade to Angular 1.5 and start rewriting the code to modernize it to add these components. Now, after that, we could add Angular 6 via the ng-upgrade library. So it allows you to run these both applications next to each other, and it allows you to kind of upgrade and downgrade your code. Let's say that I've got a service written in Angular 1. I can just use function called upgrade, and it allows me to use the same service Angular 6 code. And on the other hand, if I have a service that is written in Angular 6, I can call downgrade function, and it allows me to use this service in Angular 1. This is really wonderful concept because imagine three of your dependencies. It doesn't force you to go any path. You can migrate every single piece. It can be the leaf of the tree. It can be the root of the tree. You can migrate any piece you want, and then you can just still use it in both the old and the new code. However, ng-upgrade is not nearly as perfect as it sounds. If you want to run both Angular JS and Angular Next to each other, your application will be huge. And this is a big problem, especially if, as in our case, you have customers all over the world. We've got lots of users in countries like Singapore where the internet speed is not a problem. However, we've got also a lot of customers in Brazil where not only the internet is slow, but also the CDNs don't have that many endpoints. So the size, the increase in size was huge, and it was just when we added this new framework. If we start adding more libraries to Angular application because we need some libraries, like, for example, for translations, etc., then this will be even bigger. So this is not something that we wanted to do. So we had to add another step to our migration, which is reduce the size of the application. Now, how do you do that? So there's a few things that you can do to reduce the size of the application. First thing was to use the Webpack Bundle Analyzer library. This is a wonderful tool that allows you to see what takes that much space in your application. Well, unfortunately, in our case, when you look at the size, the AngularJS framework and the Angular framework together take most of the size. So there is not that much that we can cut. But still, you can remove some unused code. For example, in our case, for each of the tenant in our application, we started loading dependencies more dynamically. Before, every tenant loaded all the dependencies, even if they didn't need that. But now, we added the customization, which allowed us to save around five percent. Then we started replacing libraries with lighter equivalents. So, yeah. For example, we used to load, whoa. Okay. Yeah. So I mentioned already there for each tenant that we don't need all the code. So it's not only about dependencies, but also about our application code. Now, Moment.js, this wonderful library, by default, loaded more than 100 kilobytes of locales. So if your website supports 50 languages, then that's awesome. If it supports one language or two or 20 languages, you don't need all of that. So we use at most 20 locales, and that depends on the tenant. So we also added dynamic management of that. So now, we only load the locales that we need, not all of them. Then if you check your code base, I'm sure that you will find a lot of other dependencies that are just very heavy. So, for example, jQuery is something that you will find in lots of places, and it's heavy library. And right now, while it is still very useful, you might find that there's lots of equivalents or lots of lighter libraries that will provide you all the functions that you need. The same with jQuery UI. So jQuery UI is a bit more customizable because it has different components, but it still will take at least 50 kilobytes minified. Then the next thing is that you need to check dependencies of your dependencies. Even if you don't use jQuery directly, there will be some library in your code that comes from 2011, when everything depended on jQuery. So it also depends on jQuery. So, for example, the slick carousel itself takes 40 kilobytes, but it also relies on almost 100 kilobytes from jQuery. So if we want to remove jQuery, we have to first remove slick carousel. Then there's lots of libraries that are just kind of wrappers over another library. So we use the Angular counter library that when we check the code, it appears that it was just 77 lines of code, but that's because it was only a very thin wrapper on jQuery animations. So this is something that we learned that we need to always check the dependencies of our dependencies. Now, the next thing that we could use is Angular ahead of time compiler. Let me very briefly describe the difference. So whenever you deploy your application in Angular, the code includes a compiler that allows the application to get the templates that are written in Angular style. This is kind of something like JSX for React. Basically, you use things like four loops in your templates or ifs, et cetera. So by default, Angular comes with the just-in-time compiler. And just-in-time compiler compiles the templates when it needs them, which means that it needs to be present in the browser. So when the user opens the page, they download the JavaScript file. The compiler is there. And the Angular compiler adds around 100 kilobytes to a zipped file, which is quite big. But because of that, you can just send the Angular templates to the browser and the compiler will understand them. On the other hand, AOT, which is ahead of time compiler, is run on the server. So it allows you to compile all the Angular templates, and it will just inline them in JavaScript. The application should be faster because then whenever a template is needed, you don't need to fetch it from the server and compile. It is just already there. Potentially, the size of the application is lower, but that's not always the truth. Now, the problem with AOT compiler is that all templates must be available as HTML files during compilation. So in our case, the HTML is generated by our backend application written in Ruby. And it depends on the tenant. So we don't have HTML files generated statically. And even though we could do that, it is very time-consuming and quite challenging. Also, the AOT compiler understands only subset of JavaScript. There is a long page on Angular documentation that describes what is allowed and what is not. And the last thing is that if this compiler needs to inline all the templates, then it means that the templates are not fetched when they are needed from the server. It means that they are already in the application. So the question is whether you're actually making the application smaller or bigger. If your templates compiled, put into JavaScript, will take more than 100 kilobytes, then the size is the same, or the size can be bigger, and you will not have this advantage anymore. The last thing that I want to talk about is the lazy loaded modules. So Angular allows you to load the whole modules whenever they are needed. So basically, just imagine that you have a core.js file. And then later, if you want to go, I know, to my account page, it can just fetch the whole module responsible for the my account page. Unfortunately for us, initially it doesn't offer us help, because right now, when we start, we have just one module of Angular 6 application, which is the main module, which we have to load anyway. And our application is already huge. So even though we don't have any modules besides the main one, our application is already big. We still consider it for the future performance improvements, just because it should make the page faster. So eventually, we decided to go with these two options for now, to remove the unused code and find the lighter equivalents of libraries. So the migration path was to rewrite each AngularJS controller as a component, migrate services from AngularJS to services in Angular. The syntax is slightly different there. The same with concept of directives. And finally, we have to find equivalent libraries for the new framework. So what is the problem here? Of course, AngularJS libraries will not work in Angular. Lots of authors abandoned their old libraries, and either wrote new ones or didn't write new ones. So usually it is quite easy to find an equivalent library, but rarely it is a drop in replacement. In 90% of cases, there is something that you need to change to use the new version of the library. And now the last step will be that once all the code is migrated to Angular, then we can remove the breach library, the ng-upgrade, and we can finally remove the AngularJS framework entirely. So just to rephrase the challenges that we have with Angular migration is the application size, is the finding replacement libraries. The next one is that there's a lot of code to rewrite. So even though certain things are reusable, there's still a lot of changes in the syntax. And there's a lot of new concepts to learn, because the new framework introduces just quite a lot of new stuff. The reason so far is that we're still in the process of migration, and it will take us a while. But so far, we notice that the new framework is obviously much, much better than the old one. I think that the Angular code developers took a lot of concepts from React, and things like components are a huge improvement. Also, they removed the root scope, which was one of the biggest pains to work with. So we're doing that with, as I mentioned in the beginning, there is a lot of things that are going in our migration. Some of them, like TypeScript, Angular, or Docker containers are still in progress. Some of them, like Yarn or Webpack, are already completed. So the most important lessons for me are that it's better to migrate iteratively whenever possible. The fact that we waited quite long with the whole migration made it more difficult. And of course, if you wait, then you have to migrate bigger pieces of code, right? So I think that iterations and regular maintenance are extremely important. And finally, always review the libraries that you out to your call. Because even though the library can be very simple, it may happen that the dependencies there are heavy, or they may have some security issues. So that's it for me today. Thanks for coming and listening. And if you have any questions, I'd be happy to answer now or later.