[Discussion] What are your "Best practices" when building fast and maintainable processes with the architect?

When developing processes, there are a few recurring design decisions. I want to make a start by posting a few heuristics that I use regularly and found to be useful. Most can be related to more general programming patterns and are not Novulo specific.

Please let me know if you use similar heuristics so we can learn from each other! :slight_smile:

  1. Use (more) functions: If you use expressions in decisions and as the parameters for process components and plugin actions, you might reuse the same expression, or a part of it, multiple times. For example, you could define a small expression to determine whether a record status is “Finalized”. Instead of rewriting this expression, add it as a function to the record. If in the future the logic of “Finalized” will change, then you only have to update one place. (Don't repeat yourself - Wikipedia)
  2. Use (more) process components: If any process logic is used in more than one place, encapsulate it in a Process Component. This way, you only have to update it once if you want to change it in the future (Don't repeat yourself - Wikipedia). Also, it makes your processes much more readable.
  3. Limit decision-nesting for maintainability: Never nest deeper than three decision-layers within a process component. Place underlying “sub-processes” in their own process component with a clear name to improve maintainability, even if the process component is only called once. What are decision-layers?
  • With one layer, you have one decision with two possible outcomes.
  • With two layers of nesting, you have one main decision, and each branch includes at least one additional decision, resulting in four different process branches.
  • With three layers of nesting, the decision tree becomes complex with 8 different process branches
  • With four layers, you get 16 branches, which is a lot to handle when trying to maintain a process.
  1. Use sanity checks on input parameters: If a process component has parameters, check if each parameter is not NULL or whether they have some other weird value that would result in undesired outcomes. Do not expect the “happy flow” as your process can be called by anybody or by a parent process that acts weirdly. Throw an error as early as possible, and return a descriptive error message that makes debugging easy. E.g.: “Cannot execute process ‘XXX’ because parameter ABC was not set.”
  2. Cache once instead of querying twice: If a value is used in more than one place, cache it by using the “CacheValue” action of the “Process Variables” plugin. The fastest query/expression is the one that does not have to be executed. After retrieving a cached value, also perform a “null check,” as it may require special handling in such cases.
  3. Prevent slow expressions by splitting them: For example, if you want to retrieve a list of a sales that were created after the last time when product data was updated. You could write this in one expression like so:
    {sales, this, createdat.isgreater(datetime:first({products,modifiedat,true,modifiedat.desc})) }
    However, this can be slow as the sales and the products table are both quite big and the sub expression has the be evaluated for each sales. As the sub eypression is not depending on the parent sales, add the calculation of the date to an CacheValue and reference it:
  • “CacheValue - DateTime”: datetime:first({ products,modifiedat,true,modifiedat.desc })
  • "CacheValue - Sales list ": {sales, this, createdat.isgreater(cache_value_datetime.value)) }
  1. To be continued…
2 Likes