3x Smaller Lambda Artifacts by Removing Junk From node_modules

Reducing artifact size proportionally correlates with the cold start latency.

The most obvious way would be to use webpack and do tree shaking. But I’m not a fan of that approach. It’s cumbersome to set up and has significant dev effort, especially when you face issues with bundling some backend dependencies.

I’ll talk about some “quick wins” instead.

We’ll use a tool called fdu to inspect junk in node_modules.

We’ll take one of my repos as an example When installing only production dependencies, the size of node_modules folder is 149.62 MB (36.1 MB zipped). At the end of the article, we’ll reduce it down to 62.12 MB (12.2 MB zipped).

Don’t bundle aws-sdk

This is the most obvious suggestion but still brings the most value. It helped me to reduce my zip artifact size by 7 MB.

In case you don’t need the latest version of aws-sdk — just drop it from your artifact size.

Not adding it to package.json is not an option, because your tests might depend on it, and additionally, other dependencies might still install aws-sdk without you knowing it.

We’ll combat this problem in three steps.

Track currently available aws-sdk version in Lambda runtime

AWS maintains a page with the current aws-sdk version here: https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html

I’ve created an automatic alert to notify me when AWS puts a new aws-sdk version inside Lambda runtime. I like the Distill Chrome Extension, it’s free.

Now we know for sure on which aws-sdk version we can rely upon in our code.

Use yarn resolutions to ensure only 1 version of aws-sdk is installed

This is a wonderful feature if you’re using yarn. Add these lines to your package.json and yarn will install only 1 instance of aws-sdk dependency, regardless of what other packages require.

"resolutions": {
  "aws-sdk": "2.712.0"
}

Exclude aws-sdk from final artifact zip

This step might depend on the way you create a zip artifact, but it looks for me as:

zip --quiet -r --exclude="node_modules/aws-sdk/*" \
  artifact.zip \
  lib node_modules

Exclude TypeScript typings

This step helped me to remove 16 MB of junk (2.2 MB zipped) from the final artifact zip.

Whether you use TypeScript or not, many package ship typings to the NPM. They’re useful during development but have no impact on the runtime.

Here is a command to remove them:

npx del-cli \
  "node_modules/**/@types/**" \
  "node_modules/**/*.d.ts" \
  "node_modules/**/.yarn-integrity" \
  "node_modules/**/.bin"

Exclude tests & browser builds with .yarnclean

This step reduced node_modules size by 19.5 MB (14.6 MB zipped).

Another great yarn feature — .yarnclean file. You define glob patterns and yarn removes them from node_modules when running yarn install.

What type of stuff is useful to remove?

  • test files (*.test.js) — surprisingly many npm packages publish their tests to NPM. And you keep deploying this junk to Lambda runtime.
  • IDE configurations: .idea/* , .vscode/*, .eslintrc, etc
  • Graphic assets used for illustration purposes in README.md
  • Browser builds: *.min.js, dist/*.js , etc

Just add a .yarnclean file to your repository with the list of glob patterns and enjoy size savings out of the box!

You can initialize a fresh config by running yarn autoclean --init, or use my own collection of junk patterns I collected over the past year below.

I compiled my own .yarnclean file with the junk I found in my repos: https://gist.github.com/vladholubiev/1c0e9fd4d569446cada21e4a3c64d0f8

Before & After

Bonus: Add more dependencies to yarn resolutions

It might depend on the codebase, but my projects and the dependencies I use depend on lodash a lot. When running yarn install I end up with many different versions of lodash installed, which differ only by patch increment.

Here how it looks like in my yarn.lock file.

lodash@4.17.15:
  version "4.17.15"
  ...

lodash@^4.17.11
  version "4.17.11"
  ...
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
  version "4.17.20"
  ...

This is slightly risky, but I am ok with using 1 single lodash version in my repo to reduce Lambda size. Just update your resolutions section of the package.json file to look like:

"resolutions": {
  "aws-sdk": "2.712.0",
  "lodash": "4.17.20"
}

After this change, I won another 15.5 MB of space!

Keep lurking inside your yarn.lock file to find other inefficiencies.