Writing Graph Engine Friendly Code

Graph Engine is a powerful tool for code analysis, but it has its own set of limitations. Learn about Graph Engine’s nuanced architecture and how to make small refactors to your code to significantly improve Graph Engine's performance.

Some suggested refactors in this guide can conflict with accepted best practices for Apex. Read through the options, and update your code as appropriate.

If your codecase is highly complex, and you tried the existing mechanisms for avoiding timeouts and path expansion limits in Troubleshooting, this guide is for you. Use the suggestions in this guide to refactor your code, be more compatible with Graph Engine's architecture, and find performance gains.

At a high level, Graph Engine identifies entry points into your codebase. Next, Graph Engine analyzes these entry points by using them to build paths through the code and traverses those paths to apply rules.

Two factors directly affect Graph Engine’s performance.

  • The number of entry points analyzed. Entry points are evaluated in parallel, and the timeout is applied to each entry point separately.
  • The number of paths each entry point constructs. Building paths is the most expensive part of analyzing an entry point. Typically, an entry point causes timeouts or memory issues because it constructs an inordinate number of paths.

Based on these factors, the most consequential refactoring that you can do is to decrease the number of paths a single entry point constructs or to more evenly distribute paths between entry points.

To understand how to decrease the number of paths in a given entry point, let’s review this example to see how paths are built.

Assume the value of parameter b1 and b2 are unknown. Graph Engine identifies four unique paths through the method. Why? When the values of these two parameters are unknown, both if expressions have an indeterminate outcome. One path must fork into two and then into four.

In other words, where possible, Graph Engine avoids building impossible paths.

If an entry point’s complexity causes it to encounter timeouts or memory problems, the easiest way to resolve these errors is to refactor that entry point into two separate entry points with fewer paths. These entry points are analyzed in parallel and have separate timeouts, complete faster, and are less likely to exceed time or memory limits.

Refactoring entry points to redistribute paths is low effort and low risk, but it's also powerful in its ability to make your code more compatible with Graph Engine.

Let’s look at an example with an Aura-Enabled method. Graph Engine considers this method an entry point.

The values of all three parameters, b1, b2, and b3, are unknown, so each conditional if expression causes paths to fork. If we assume that each helper method contains exactly one unique path, then the total number of possible paths is 8, or 1 x 2 x 2 x 2. That number of paths is unlikely to exceed time or memory limits.

Now let’s use the same example but assume that each helper method contains 10 unique paths. Then each conditional if expression actually creates 20 unique paths instead of just two. The total number of paths is then 8,000, or 1 x 20 x 20 x 20, which is an excessive number of paths.

Let’s refactor the code from our previous example. We turn the single entry point into two entry points that include fewer paths. Our code now looks something like this.

Our refactored code with redistributed paths has two Aura-enabled entry points, foo1 and foo2. Each of these entry points invokes innerFoo by passing in their own parameters and a literal boolean to innerFoo, which is identical to the old foo.

Each entry point uses a definitive value for b3 in its invocation of innerFoo, so each entry point avoids forking at the final if. That means half as many paths are evaluated. If we assume that each helper method has 10 unique paths, each entry point builds only 4,000 paths, or 1 x 20 x 20 x 10.

Entry PointsUnknown ParametersIndeterminate if ClausesPaths Per Helper MethodPaths Per Entry Point CalculationTotal Paths Per Entry Point
One Inefficient, One Path3311 x 2 x 2 x 28
One Inefficient, Ten Paths33101 x 20 x 20 x 208,000
Two Sufficient22101 x 20 x 20 x 104,000

By refactoring the code from the inefficient example into two efficient entry points, we cut the total paths per entry point from 8,000 to 4,000. The new entry points are far less likely to time out than the original. Because they can be analyzed in parallel, they finish sooner.