Build Optimization Through Minimal Inclusion

In Adobe Experience Platform Launch, we’ve worked hard to make the code we ship to your website as small as possible. We want to keep your website humming at a fast clip while still providing all the functionality you depend on. One of the ways we do this is through minimal inclusion.

To get a better idea of how this works, it’s important to know what’s typically included in the code we ship to your website (often called a “build”):

  1. Configuration: This is the information you provide to Launch while installing extensions, creating rules, creating data elements, etc.
  2. Rule engine: We affectionately call this Turbine. It’s open source and the code can be found here. Its job is to coordinate everything. It executes rules, caches data element values, sets up the _satellite object, provides debugging utilities, and so on. However, a lot of the functionality of events, conditions, actions, and data elements is delegated to extensions.
  3. Extension library modules: This is the code that extensions provide that detects events, evaluates conditions, performs actions, and retrieves data element values. Again, all of this is coordinated by the rule engine. Some extensions are written by teams at Adobe, while others are written by third-party technology vendors.

When you create a library build in Launch during the publishing process, the output contains all these things bundled together. However, our build system intelligently includes only the pieces of code that may actually be executed.

In Launch, every new property comes with the Core extension installed by default. The Core extension provides a plethora of common event types, condition types, action types, and data element types that users often need to accomplish their goals. You may think all of this functionality is included in a library build, but that’s not the case. If you use a date range condition in a rule that’s in your library, the condition’s associated code (the logic that performs the evaluation of the condition while running on your website) will be included in the built file. On the other hand, if you don’t use the date range condition, its code will not be included in the built file. The condition would never be run in that case, so it’s considered dead weight and not included.

This strategy of only including necessary code doesn’t only pertain to the Date Range condition in the Core extension; it applies to all event types, condition types, action types, and data element types provided by any extension installed on your property. This should be seamless to you. Launch is the friendly janitor working in the background, keeping things clean and tidy while you do what you do best.

Behind the curtain

If you’re an extension developer, you may want to sneak a peek behind the curtain. Don’t be shy. You’re invited — encouraged even! Here’s what it means for you.

Let’s say we’re building an extension that provides a Screen Resolution condition type and a Window Size condition type. Our extension.json file looks something like this (with a lot trimmed out):

{
...
"conditions": [
{
"name": "screen-resolution",
"displayName": "Screen Resolution",
"libPath": "src/lib/conditions/screenResolution.js",
...
},
{
"name": "window-size",
"displayName": "Window Size",
"libPath": "src/lib/conditions/windowSize.js",
...
}
],
...
}

When a Launch user uses our Screen Resolution condition type in a rule, we can be sure that the code within /src/lib/conditions/screenResolution.js will be included in the emitted Launch build that will run on the user’s website. On the other hand, if they don’t use our Screen Resolution condition type, the code will not be included. Again, this same strategy applies to the Window Size condition type and the code within /src/lib/conditions/windowSize.js.

Now, assume we have a utility that we use to do some number comparison logic and we’ve created it at /src/lib/conditions/utils/compareNumbers.js. Let’s also consider that both the screenResolution.js and windowSize.js use this utility. In case you’re wondering, this would be done using a require() statement from within both screenResolution.js and windowSize.js as demonstrated here:

var compareNumbers = require('./utils/compareNumbers');
module.exports = function(settings) {
// Use our compareNumbers function here when evaluating
// the condition.
};

Assuming screenResolution.js and windowSize.js both use compareNumbers.js, when would our compareNumbers.js code be included in the user’s library? Whenever it’s needed! To illustrate, here are a few scenarios and the resulting inclusions:

Launch user uses the Screen Resolution condition but not the Window Size condition:

  • screenResolutions.js is included
  • windowSize.js is not included
  • compareNumbers.js is included

Launch user uses the Window Size condition but not the Screen Resolution condition:

  • screenResolutions.js is not included
  • windowSize.js is included
  • compareNumbers.js is included

Launch user uses the Screen Resolution and Window Size condition:

  • screenResolutions.js is included
  • windowSize.js is included
  • compareNumbers.js is included

Launch user uses neither the Screen Resolution nor the Window Size condition:

  • screenResolutions.js is not included
  • windowSize.js is not included
  • compareNumbers.js is not included

Minimal inclusion is one technique we use for optimizing code, but it’s not the only technique. If you’re interested in learning about one of our other techniques, check out our Optimizing JavaScript Through Scope Hoisting article to learn how we combine and minify code for additional performance gains.