cdktf compile optimization with swc

Laying the grounds works, there are a number of tools in analysis here:

I am interested in using the cdktf, with typescript, to enhance infrastructure as code declarations for ease of use.

Seeing large projects take on the challenge of changing their compiler made me think if it was worth it; what are the pros and cons.

Summary

Sample code can be found here: https://github.com/simonireilly/cdktf-swc

result chart

Trade-offs:

SWC vs. TSC

Compiler Command Mean [s] Min [s] Max [s] Relative
swc yarn synth 4.9 ± 0.2 4.7 5.2 1.0
tsc yarn synth 28.0 ± 3.8 24.9 32.2 5.6 ± 0.8

TSC incremental vs. SWC

Compiler Command Mean [s] Min [s] Max [s] Relative
swc yarn synth 5.098 ± 0.439 4.661 5.540 1.07 ± 0.12
tsc --incremental yarn synth 4.744 ± 0.341 4.360 5.008 1.00

Read on if you want to know about the implementation details

Setup

There is a guide here I followed for getting started with cdktf: https://learn.hashicorp.com/tutorials/terraform/cdktf-build?in=terraform/cdktf

This isn't really the subject of the blog, but it is a nice intro to get you started if you plan on using the cdktf.

Experiencing slow synth

I was doing some development on the train, and I was thinking, wow, this seems slow. When I discovered this was actually the compiler, not the network I was pretty shocked.

Digging under the hood there is actually some fairly good reasons this feels like a slow experience, and there are a number of solutions.

  1. The cdktf was setup to re-compile the code, including all files in the .gen folder, on every synth.
  2. The cdktf was setup with a tsconfig.json that wasn't using incremental builds (tsc keeps a build manifest to help it detect relevant changes and only recompile what is necessary).
  3. The cdktf was setup with a tsconfig.json that would produce type declarations (*.d.ts); which is super useful if you are distributing your code for use in js environments. If you are not, and you are just using typescript, you might not need these.

Solution 1

Just set incremental to true in the tsconfig.json.

I am not going to lie, this is the simplest, and biggest win for compile time, and improves the overall performance straight away.

{
"compilerOptions": {
"incremental": true
}
}

Solution 2

Since the initial build is still slow, another transpiler, swc, could be used to speed up builds:

yarn add --dev @swc/core @swc/cli
// .swcrc
{
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2018"
},
"sourceMaps": true,
"module": {
"type": "commonjs",
"strict": false,
"noInterop": false,
"strictMode": false
}
}

Update the compile script to use swc:

{
"scripts": {
"compile": "swc main.ts --out-file dist/main.js & swc .gen/providers/**/*.ts -d dist/.gen/"
}
}

Analysis

I used hyperfine on my local machine:

$ lshw -short
H/W path Device Class Description
====================================================
system Latitude 7490 (081C)
/0/4d/0 memory 16GiB SODIMM DDR4 Synchronous Unbuffered (Unregistered) 2133 MHz (0.5 ns)
/0/4d/1 memory 16GiB SODIMM DDR4 Synchronous Unbuffered (Unregistered) 2133 MHz (0.5 ns)
/0/54 processor Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz

TSC --incremental vs. SWC

Testing against incremental, we allow for the build to be present before each run:

hyperfine 'COMMAND=compile yarn synth' \
'COMMAND=build yarn synth' \
--export-csv results/complete.csv

TSC vs. SWC

To test without incremental we perform cleaning of the dist directory between runs:

hyperfine 'COMMAND=compile yarn synth' \
'COMMAND=build yarn synth' \
--prepare 'yarn clean' \
--export-csv results/complete.csv

Wrap-up

Thanks for reading the methods, hopefully this is useful analysis if you are considering implementing swc of tsc --incremental.