What Are We Going To Learn?
- What are the Angular application budgets
- What kind of problems can be discovered by using budgets
- How can budgets help with these problems
- What are the tradeoffs ( cost of using budgets in your application )
- How to prevent problems uncovered by budgets to ever happen again
This article is based on a true story! It happened while working on the Angular NgRx Material Starter which recently started using Angular CLI budgets…
What is Angular CLI Budgets?
Budgets is one of the less known features of the Angular CLI. It’s a rather small but a very neat feature!
As applications grow in functionality, they also grow in size. Budgets is a feature in the Angular CLI which allows you to set budget thresholds in your configuration to ensure parts of your application stay within boundaries which you set — Official Documentation
Angular budgets allows us to configure expected sizes of these bundles. More so, we can configure thresholds for conditions when we want to receive a warning or even fail build with an error if the bundle size gets too out of control!
How To Define A Budget?
Angular budgets are defined in the
angular.json file. Budgets are defined per project which makes sense because every app in a workspace has different needs.
Thinking pragmatically, it only makes sense to define budgets for the production builds. Prod build creates bundles with “true size” after applying all optimizations like tree-shaking and code minimization.
budgets property is an
array which accepts list of budget configuration objects. While there are many possible ways to configure budgets, we should be most interested in configuration per bundle. That way we get the most targeted feedback in case of crossing of a threshold and we will have much easier time figuring out what is the root cause.
Every budget of a type
bundle needs a
name so that it can be matched to the corresponding bundle. The
baseline property describes the expected size of a bundle.
error properties specify how much can the bundle size deviate from its baseline. For example bundle with the
baseline of 100kb will trigger
warning of 50kb only if its size is more than 150kb or less than 50kb. The warning then acts as a both min and max threshold.
It is also possible to configure min and max thresholds individually using
maximumWarningproperties (same for error)
Follow me on Twitter to get notified about the newest Angular blog posts and interesting frontend stuff
What Can Go Wrong?
Ok, our budget configuration is in place and we start working on some new feature. After some hours we try to run the prod build and get an output like this…
Oops, a build error! The maximum bundle size was exceeded. This is a great signal that tells us that something went wrong…
- We might have experimented in our feature and didn’t clean up properly
- Our tooling can go wrong and perform a bad auto-import, or we pick bad item from the suggested list of imports
- We might import stuff from lazy modules in inappropriate locations
- Our new feature is just really big and doesn’t fit into existing budgets
- Something else? Please, share stuff which has caused Angular budgets errors in your applications using the article responses ✏️😉
In case from the “true story”, the problem was caused by a bad import of an operator from RxJS — a 3rd party library which is inseparable part of every Angular application.
How Will Budgets Help?
Our bundle size has increased significantly and we can be pretty sure that something went wrong. What can we do to debug this problem in greater detail? Enter Webpack bundle analyzer or for the more practical-minded folks,
npm i -D webpack-bundle-analyzer.
This module enables us to “Visualize size of webpack output files with an interactive zoomable treemap” — Official Docs
It is extremely useful for exploring code that ends up in particular bundle as a result of a build process. We can run it in our Angular CLI applications by running prod build with additional
--stats-json flag which generates
stats.json file. The file can be then consumed by the analyzer using the following command
Check out an example of how to implement the analyze functionality using npm scripts in Angular NgRx Material Starter (focus on
The output of the analyzer contains all modules but we’re only interested in things which don't look quite right. Stuff like major duplications or modules in a bundle where they don’t belong.
In case from the “true story”, the analysis of the bundles content helped to localize problematic import by providing hints about the library (rxjs) and package name (internal)
Budgets Sound Great, What Is The Catch?
As the popular saying goes, nothing is for free…
Especially in the field of software engineering, every decision we make results in some kind of a trade-off. So what is the cost of maintaining budgets? To keep it short, it’s pretty low…
First, we have to establish initial budgets configuration based on the current size of all relevant application bundles and their error and warning thresholds.
I would suggest focusing on simplicity instead of being 100% precise. It’s faster to look over budgets with round numbers like
700kbinstead of hurting your eyes by scanning large amount of random numbers…
Budgets with reasonably set warning and error thresholds shouldn’t get triggered during the course of a normal development
Adding a new minor feature or fixing a small bug will probably NOT result in tens of kilobytes of extra bundle payload.
On the other hand, adding of a new fancy 3rd party library just might set the budgets alarm off and that’s a good thing…
- It can makes us aware of the real cost of using that tiny utility which pulls in the rest of the library
- It can lead into a team discussion about how to address the need for this specific functionality (eg do we really need to add a dependency or would it be easier to implement it ourselves?)
Once we are aware of the reason for a bundle size increase and we’re happy with it all, we have to do is to simply adjust budget with the new values …
Permanent Solution To Problems Found By Budgets
In our previous example, the budgets were catching bad RxJS import which caused huge increase in the bundle payload size. Removing the bad import fixes current problem but doesn’t really help with any future occurrences of similar problems.
Luckily, it is possible to update related tslint rule called
import-blacklist with the offending import
rxjs/internal/operators. This is a permanent solution to the problem uncovered by the budgets. Great!
We established that the budget errors can arise as a result of bad imports of 3rd party libraries. Adding to that, bad imports can happen also in the application code itself. Especially in very large enterprise applications.
Imagine a situation where we have couple of large lazy-loaded modules. What if we tried to import and use service from a lazy module in the eagerly loaded parts of application? I mean, of course just by a mistake. 😉
Angular compiler would have no other choice than to pull that service and all related dependencies into the main bundle!
Depending on a particular situation this could lead into significant increase of the main bundle size. On the other hand, such a problem is much harder to detect and debug compared to weird looking imports of 3rd party library.
I am aware only about one open source solution that can help us to prevent this kind of problems. The solution is using
nrwl/nx workspaces. It provides us with the ability to assign tags to our own apps and project libraries. This tags enable us to define rules about what can be imported from where with a provided
nx-enforce-module-boundaries Tslint rule. It might be an overkill for a small apps but can definitely pay off in an enterprise environment!
Angular budgets are great! Use them and keep your apps lean and fast!
That’s It For Today!
I hope you enjoyed this article! Please support this guide with your 👏👏👏 to help it spread to a wider audience 🙏. Please, don’t hesitate to ping me if you have any questions in the article responses or on Twitter @tomastrajan
And never forget, future is bright
If you made it this far, feel free to check out some of my other articles about Angular & frontend software development…