Getting Serverless Framework applications to production as fast as possible is the serverless-jetpack plugin's unending quest. Our adventure now leads us to an exciting new development that offers:
- 🚀 faster-than-ever-before packaging speed
- 📦 the smallest possible application package
- 🤩 25x faster and 4x smaller builds in real projects
Let's meet Jetpack's new tracing mode feature.
A packaging journey
Out of the box, the Serverless Framework bundles up the code in a Node.js application using a series of pattern inclusions/exclusions and implicit exclusion of unneeded development dependencies. The serverless-jetpack
plugin dramatically speeds this up with blazingly fast production dependency discovery, producing equivalent packages often over 10 times faster.
Hitting the wall
Unfortunately, the underlying dependency-based Serverless approach can only go so far and so fast. For example, in one of our recent client projects, a seemingly straightforward Serverless project faced egregiously slow build times of over seven minutes for a single package.
Digging into the details, we discovered that the application contained overly broad production dependencies
in package.json
(directly and transitively) that needlessly filled the application package with extra files. The vast majority were completely unused at runtime—including development-only big hitters like webpack
and babel
.
After itemizing the package contents, we discovered that of the over 20,000 files included in the application package, less than 900 files were actually used at runtime!
Big is slow
Adding unused files to a Serverless application package hurts on multiple levels:
- Packaging: Additional files take up more file I/O for file discovery and package copying, and consume more CPU for zipping up a large application package.
- Deployment: Larger zip packages take more network time to deploy from your build server to the destination cloud.
- Execution: Larger application code size can even slow down the execution of your function at runtime in the cloud. See discussions here and here.
Thus, even though our client project used Jetpack's faster approach and additional enhancements like parallel processing, it was still ridiculously slow to build. And, our AWS Lambda code package was significantly larger than it needed to be.
What we really wanted was some way to find and package just the code that our handlers needed and nothing else. We brainstormed, researched, and experimented, and are pleased to present the result: serverless-jetpack
's new tracing mode.
Tracing just what you need
Jetpack's tracing mode is an alternative way to package Serverless applications. To try it out, install serverless-jetpack
and enhance your serverless.yml
configuration with:
plugins: - serverless-jetpack custom: jetpack: trace: true
How it works
Packaging in tracing mode starts with each Node.js function's handler source file. Jetpack parses each source file and extracts all statements that indicate a dependency on another file like: require()
and import
. These files are recursively traversed to create a complete list of the files actually used in the original handler, which Jetpack includes in a Serverless application bundle instead of package.json
production dependencies.
Best practices
When using tracing mode, there are a few tweaks you'll likely want to make to a standard Serverless Framework configuration file. Because tracing mode automatically packages all discoverable imports, package.include|exclude
patterns are only really needed for additional files that aren't statically traceable—like dynamically imported code, etc. It is also a great opportunity to use Jetpack's jetpack.preInclude
feature to exclude everything to start, and then package.include
for the few extra things handler tracing can't discover.
The following example uses a few tracing mode options for a tight, fast build (with comment hints for the order of inclusion):
# serverless.yml plugins: - serverless-jetpack custom: jetpack: trace: true preInclude: # STEP 1: Start with nothing. - "!**" package: include: # STEP 3: Manually add dynamically-require'ed # config files to the package - "config/**" functions: hello: # STEP 2: Trace code files starting at `src/hello.js` handler: src/hello.handler
Gotchas
Tracing mode produces Serverless application packages that are different (notably smaller) than vanilla Serverless or Jetpack in dependency mode. While the packages should still correctly execute in the cloud, we recommend that you review the known tracing mode caveats to make sure that your Serverless application is a good fit for tracing mode and that everything will work once integrated.
Smaller and faster!
We reviewed Jetpack's tracing mode against both Serverless' built-in packaging and Jetpack's existing dependency mode. For projects with large numbers of production dependencies the results were impressively in favor of tracing mode. Let's review a couple of these scenarios:
- Jetpack's test fixture
huge-prod
: A contrived scenario with lots of production dependencies that are unused by the function handler. - Client GraphQL project: A client's Serverless-based GraphQL endpoint.
- Client Server project: A client's Serverless-based Next.js application server.
Smaller!
Let's first look at the size improvements using Jetpack's tracing mode versus Serverless built-in packaging (which is the same as Jetpack dependency mode):
Project | Scenario | Type | Package Size | vs Base |
---|---|---|---|---|
Jetpack Test | huge-prod | jetpack trace | 0.5 mb | -92.27 % |
Jetpack Test | huge-prod | serverless | 6.6 mb | |
Client | GraphQL | jetpack trace | 13.1 mb | -76.81 % |
Client | GraphQL | serverless | 56.5 mb | |
Client | Server | jetpack trace | 21.0 mb | -73.75 % |
Client | Server | serverless | 80.0 mb |
In these scenarios, Jetpack tracing mode was able to get a package zip file size reduction of between 4 to 13 times smaller!
Faster!
With fewer files to discover on disk and copy over to the Serverless zip package, tracing mode packaging times are quite impressive (even when comparing our client projects that already used Jetpack dependency mode):
Project | Scenario | Type | Time | vs Base |
---|---|---|---|---|
Jetpack Test | huge-prod | jetpack trace | 8 s | -70.00 % |
Jetpack Test | huge-prod | serverless | 25 s | |
Client | GraphQL | jetpack trace | 10 s | -95.00 % |
Client | GraphQL | jetpack deps | 200 s | |
Client | Server | jetpack trace | 15 s | -96.38 % |
Client | Server | jetpack deps | 415 s |
These scenarios see packaging time speedups of between 3 to 27 times faster.
One way or another, Jetpack is what you need
Our ongoing experiences using serverless-jetpack
in real-world client infrastructures gives us confidence that it is the right solution for most Serverless application deployments.
Jetpack's new tracing mode offers substantial speedups + size reductions in common situations (overly broad production dependencies). At the same time, Jetpack's battle-tested default dependency mode still provides an always faster, drop-in replacement for Serverless built-in packaging.
All in all, we're confident that serverless-jetpack
will provide you with the flexibility to get the fastest possible Serverless application packaging and deployment. And with the introduction of Jetpack's new tracing mode, we'd love to hear which mode works best for you! 🚀