Build time benchmarking using Angular CLI
Build time benchmark of monolithic application build using Angular CLI
In this article, we’ll take a look at the impact of different build options of the Angular CLI on the build time of a monolithic application. Building is usually on a critical path of the continuous integration (CI) pipeline—that is, any changes to build duration impact the total CI time. The Angular team makes a few build optimizations available for developers, and in this post, we’ll take a look at their impact.
Measurements
To make the data reproducible and relevant, I’ll run the test builds with CircleCI. Using external servers for the build isolates the measurement from my local environment. At the same time, the results come from machines that others are using for building as well.
The key measurement I’ll pay attention to is the time needed to run the npm run build
command:
At this level, I expect to see the most direct results of the optimization we use. If we manage to cut the build time in half, then we should see this impact here.
Another value to consider is the time of a whole build job. This will be especially important for optimizations that come with positives and negatives: for example, caching can speed up some operations, but part of the gain will be eaten by the downloading and uploading cache.
The last measurement for us to consider is the overhead: the difference between build script and build job. It will contain everything that is done before or after the build: restoring the cache, installing dependencies, saving the cache. With this number, we should see clearly the downsides of some optimization.
I’ll repeat each test build 5 times under the same circumstances and use the average of the measurements as the measurement value. In this way, I’ll be able to reduce a bit of random noise in the data without turning it into a full scientific experiment.
Application structure
As a benchmark, I’m using the build time of a simple application generated and built with the Angular CLI. The application models the real world complexity by including 1000 components. This approach is inspired by Nrwl’s Angular benchmark. Each component is very simple:
src/app/component1/component1.component.ts
:
import { Component } from '@angular/core';
@Component({
selector: 'app-component1',
templateUrl: './component1.component.html',
styleUrls: ['./component1.component.css']
})
export class Component1Component {
}
src/app/component1/component1.component.html
:
<p>component1 works!</p>
And the whole application just displays each component one after another:
The biggest blind spot is the lack of third-party libraries. This is something that almost all real-world applications do, but including it in the benchmark would tie the results to the set of libraries used in the example application.
Build options
In my test, I’m measuring the build time in the following setups.
Baseline
This is the standard build as it’s generated by Angular CLI. Important features are as follows:
- it uses webpack under the hood
- it lacks
ng cache
for CI—so each build during CI starts from scratch - the build job provides cache for
npm install
esbuild
esbuild is a promising bundler with much better performance than Webpack:
according to its own website, it’s up to 100 times faster. Because of its
speed, since version 14, the Angular team has provided an experimental builder
based on esbuild. It was recently improved in version 15. It can be turned on
easily, with just 1 line change in angular.json
:
"architect": {
"build": {
- "builder": "@angular-devkit/build-angular:browser",
+ "builder": "@angular-devkit/build-angular:browser-esbuild",
"options": {
Note! So far, this change affects only the build—the development server is still using Webpack. This is likely to change in version 16.
ng caching
For local builds, Angular CLI caches build results to save time on reruns. By default, this feature is turned off for CI. To use caching during CI, we need two changes:
- changing the cache configuration from local-only, and
- reading and saving the cache during CI.
You can turn on caching with two commands:
ng config cli.cache.environment all
ng cache enable
Managing cache files for CI will require changes in configuration. You can find my change for CircleCI here.
TypeScript 5
TypeScript 5 promises up to 10% improvement for the build time. It’s not yet
available for Angular applications, but it’s on track to come in version 16.
For my tests, I’m updating Angular to pre-release version 16.0.0-next.4
.
Data
The builds I’ve run measured as follows:
npm run build (s) | Build job (s) | Overhead (s) | |
---|---|---|---|
baseline | 57.8 | 92 | 34.2 |
esbuild | 14.4 | 50.2 | 35.8 |
hot build cache | 31.4 | 67 | 35.6 |
cold build cache | 66.8 | 104.2 | 35.6 |
TypeScript 5 | 55 | 84.8 | 29.8 |
The baseline time needed to complete npm run build
was on average 57.8s. The
biggest improvement we can see is with switching to esbuild
: it becomes 4
times faster and takes only 14.4s to build. Both builds have very similar
overhead. With esbuild, the build job takes 56% of time that was needed in the
baseline case—an excellent improvement.
Another optimization—caching of the Webpack build—looks very promising as well. Because there are more files cached, I expected a clear increase in the overhead. It went from 34.2s to 35.6s, but this difference could be statistically insignificant. Caching of the build has two effects:
- speed increases when cache is “hot”—with files already in place, the build job finishes in 31.4s instead of 57.8s.
- speed decreases when the cache is “cold”—most likely, the build takes more time because there are way more files to be written on the disk. Build went up from 57.8s to 66.8s.
Lastly, an interesting case is updating to TypeScript 5. The time needed to complete the npm run build lowered slightly from 57.8s to 55.5s. At the same time, the overall build job took 84.5s instead of 92s, which is a better improvement than the build script alone. This could be explained by the more efficient installation—maybe TypeScript 5 or other v16 dependencies are faster to install.
Conclusions
- Check out esbuild—if it won’t introduce regressions to your code, you can immediately enjoy faster builds!
- If esbuild doesn't work in your case, try enabling caching for Webpack on CI.
- Don’t hold your breath for the speed increase that comes with switching to TypeScript 5.
Links
You can find our test application here.
Looking for more support for your Angular projects?
Our team at DevIntent is highly experienced in web development, including framework migration and updates. Check out our offerings or reach out.