# Optimizing (unzipped) lambda size with pre-building large dependencies

Source: https://dev.arcxp.com/pagebuilder-engine/how-to-guides/optimization-and-deployment/optimizing-unzipped-lambda-size-with-pre-building-large-dependencies/

With Engine 5.0.4, we released a new build configuration that helps optimize the compiled bundle on average 30% smaller server-side bundle size (in some samples up to 80%). See [Optimize large bundles’ build size with content source compilation dependency duplication](https://dev.arcxp.com/pagebuilder-engine/developer-docs/optimize-large-bundles-build-size-with-content-source-compilation-dependency-duplication) guide for more information.

When a PageBuilder bundle is deployed, client code (features, content sources, and so on) gets bundled together with PageBuilder’s core and packaged together to be placed into an AWS Lambda function.

AWS enforces a 250 MB lambda code size limit. This is unzipped code that runs the Engine and client bundle together.

![Engine Lambda Content](https://dev.arcxp.com/images/pagebuilder-engine/engine-lambda-content.png)

PageBuilder code, Arc XP blocks, and custom blocks code, along with their dependency packages, all have to share this space inside the lambda functions.

PageBuilder’s own dependencies and other Arc XP dependencies (Themes and Outbound Feeds blocks, for example) make it hard to predict the reserved size to enforce a size limit for custom code.

Bundles that require larger and more complex dependencies may hit these limits sooner than expected.

Arc XP’s team is actively working on optimizing the standard build process to package and compress all dependencies more efficiently. In the meantime, you can optimize bundles bumping into these limits by performing a pre-deployer build that can do simple tree shaking and minification on its large dependencies.

## Quick wins before the pre-build approach

Before reaching for the webpack pre-build, run through these two checks. Either one is often the actual cause when a bundle unexpectedly hits the size limit.

### 1\. Audit package.json for mis-classified dependencies

The Deployer installs everything in `dependencies` into the Lambda artifact. Not just what your code imports. Every package pulls in its full transitive tree.

One common source of bloat: the bundle repo is also used for another platform, like a mobile app. Packages added for React Native or other non-PageBuilder targets can quietly land in `dependencies`. They never run in the Lambda environment, but they still count against the limit.

Look for:

- **Unused packages:** if a package isn’t imported anywhere in your PageBuilder features or content sources, move it out of `dependencies`.
- **Dev and build-only packages:** test runners, linters, and coverage tools belong in `devDependencies`.
- **Framework packages Engine already provides:** `react`, `react-dom`, and `prop-types` are externalized by the Engine build. All three can go in `devDependencies`.

Run `npm ls --depth 0` to list top-level installed packages and compare against what your bundle actually imports at runtime.

### 2\. Audit what goes into the bundle zip

Make sure your CI/CD pipeline is using the official deployer script (`deployer.js` / `deployer.sh`), not a raw zip of the repo root. A raw zip will pick up large folders that serve no purpose in a Lambda deployment.

Common offenders:

- `coverage/`: Jest/Istanbul coverage reports can reach 10–20 MB
- `jest/`
- `selenium/`
- `docs/`
- `themes/`: if managed separately from the bundle

Run `du -sh * | sort -rh` in your bundle root before deploying to spot unexpected large folders.

If both checks come back clean and the bundle is still over 250 MB, move on to the pre-build approach below.

## Setting Up the Build Environment

To optimize the build process, you must first configure Webpack and install build dependencies. This guide uses `highcharts` as an example of a large dependency (40+ MB) that needs optimization.

1. **Configure Webpack**

  1. Create a `webpack.config.js` file in your root folder. The webpack configuration defines how webpack processes your dependencies:  
    - The `entry` property points to your source file (`wrapper.js`) containing dependencies to optimize.
    - The `output.path` property specifies the compilation output location (`modules` directory).  
webpack.config.js  
```js
var path = require('path')
module.exports = {
  entry: './wrapper.js',
  mode: 'development',
  target: 'node',
  output: {
    path: path.resolve(__dirname, 'modules'),
    filename: 'wrapper.js',
    libraryTarget: 'commonjs2',
  },
  externals: {
    react: 'react',
    'react-dom': 'react-dom',
    'mock-require': 'mock-require'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
        }
      }
    ]
  }
}
```
2. **Install Build Dependencies**  
a. Run the following command to install the required development packages:  
```
npm install -D webpack webpack-cli @babel/core @babel/plugin-proposal-export-default-from babel-loader
```
b. Add the pre-build script to your `package.json`:  
```json
{
  ...,
  "scripts": {
    ...,
    "pre-build": "webpack --mode production"
  },
  ...
}
```
3. **Identify Dependencies to Optimize**  
a. Run the size analysis command:  
```
du -hs * | gsort -h -r    # On non-Mac systems, use 'sort' instead of 'gsort'.
```
b. Review the output to identify large dependencies:  
```
 49M  amazingcharts
 24M  date-fns
7.2M  @babel
5.7M  es-abstract
4.9M  lodash
4.0M  styled-components
3.8M  luxon
3.6M  polished
...
```
4. **Create Dependency Wrapper**  
a. Create a `wrapper.js` file in your root folder.  
b. Import and export the identified large dependencies:  
wrapper.js  
```js
module.exports = {
    "highcharts": require("highcharts"),
    "highcharts-react-official": require("highcharts-react-official"),
}
```
> **Important:**  
>  
> Modify the `wrapper.js` file to include your project’s specific large dependencies that need optimization.  
c. Run the pre-build command:  
```
npm run pre-build
```
5. **Update Package Configuration**  
a. Move optimized dependencies from `dependencies` to `devDependencies` in `package.json`. This action prevents including raw `node_modules` versions in deployment.  
> **Note:**  
>  
> Deployer only installs production dependencies.
6. **Update Import Statements**  
a. Replace original imports with references to the compiled wrapper:  
```js
// Path to the compiled wrapper.js in the modules directory
const wrapper = require('../../../../modules/wrapper');
const Highcharts = wrapper['highcharts'];
```
7. **Test and Deploy**  
a. Test your local changes with the following steps:  
i. Remove original package folders:  
```
   ```
     rm -rf node_modules/[package-name]
   ```
```
ii. Restart development environment:  
```
   ```
   fusion stop
   fusion start
   ```
```
iii. Verify page rendering.  
b. Deploy your bundle with the following steps:  
i. Deploy your bundle to a non-production environment.  
ii. Test in preview mode.  
iii. If successful, proceed to production deployment.

## Example Results

Tests of this optimization process with the highcharts library (40+ MB disk space) give the following results:

- Original deployment:

  - Total lambda function size: **145 MB**
  - node\_modules size: **95 MB**
- After optimization:

  - Total lambda function size: **93 MB**
  - node\_modules size: **41 MB**

This optimization frees approximately **50 MB** of space by applying the technique to a single large package dependency.

## Consider splitting your bundle with Micro Experiences

If the pre-build workaround is something you are running on every deploy, the bundle has grown past the point where size optimization alone keeps up.

Micro Experiences (MX) lets you split into multiple PageBuilder instances, each with its own codebase and Deployer. Smaller bundles mean smaller Lambda footprints, faster compile times, and independent deployment pipelines. A team working on subscriptions does not need to be in the same bundle as the team building sports features.

Getting set up with MX takes some planning, but if you are regularly hitting Lambda size limits, it addresses the root cause rather than working around it.

- [Introducing Micro Experiences](https://dev.arcxp.com/micro-experiences/introducing-micro-experiences)
- [Planning Your Micro Experience Migration](https://dev.arcxp.com/micro-experiences/planning-your-micro-experience-migration)
- [Micro Experiences Developer Guide](https://dev.arcxp.com/micro-experiences/micro-experiences-developer-guide)
