Basic Implementation Guidelines

FNZ Studio developers are faced with lots of choices during projects, and the FNZ Studio platform often gives you multiple implementation options to cover a wide variety of requirements.

A wide variety of options allows developers to cover a broad set of requirements in a flexible manner. However, some of these options can lead to issues when applied together.

Developing processes, data models, integrations, business rules, and UX are all highly complex knowledge work activities, which can only be mastered with years of experience. Therefore, this recipe covers the most common guidelines that should be considered when implementing an FNZ Studio solution.

What follows is a collection of guidelines and principles that anyone developing in FNZ Studio should follow to avoid the most common mistakes, which lead to 50% of the quality issues faced in projects.

We have divided these tips into nine sub-sections:

  1. General Guidelines
  2. Value Stores
  3. Packages
  4. Scripting
  5. Processes
  6. Screens
  7. Data Modeling
  8. Integration
  9. Configurations

General Guidelines

  • Understand What You Are Changing Before You Change It As tempting as it is to quickly and blindly put an 'if' condition in a Screen to remove a button, doing so leads to unmanageable complexity, and a project that is difficult to maintain. Before implementing any change to a solution you didn’t implement yourself, try to understand if the previous developer(s) planned a configuration mechanism to implement your change.
  • Add Lots of Comments Comments help future developers (and yourself after 18 months) understand the intended design, and to implement changes faster and without introducing regressions. Add a lot of descriptive comments to Business Objects. Make good use of the description fields, comment blocks in scripts, Note elements in Process diagrams and Screens, etc.
  • Avoid Using Deprecated Components Using deprecated components leads to significant reworks when upgrading the platform. Moreover, deprecated components are only subject to critical bug fixing: FNZ Studio won’t be implementing any enhancements or new features for them. Regardless of which Business Object you are editing, refrain from using deprecated components. If you are modifying a solution which already contains deprecated components, evaluate the possibility of replacing them with newer/alternative ones.
  • Stick to Your Project Naming Conventions Respecting a naming convention makes maintenance easier, makes onboarding new developers faster, and helps other people in the team troubleshoot issues on artifacts you built. Naming conventions also help when conducting code reviews and investigations. Most projects have a properly documented naming convention to respect. If there isn't one established beforehand, adopt one now.
  • Ask for Peer Reviews on Complex Logic It’s very hard to get complicated things right on the first attempt. Having two sets of eyes check your work dramatically helps improve quality. If you are implementing complex logic, have someone else in the team review the implementation.
  • Don’t Reinvent the Wheel Out-of-the-box implementations, components, and functions are more performant, are subject to maintenance, and can save you a considerable amount of development work. Before implementing a new functionality, search the FNZ Studio Knowledge Base and ask other FNZ Studio developers to try find out if the problem has already been solved. There may already be an extension or an out-of-the-box function that addresses your requirement.
  • Use the Validate Button Validation rules are defined by the Product team for each Business Object, and already implement many of the common best practices to be adopted in projects. Using the validation tool can dramatically improve the quality of the delivery. Each Business Object Editor provides a Validate button; click it before saving each Object, check the results provided, and proceed to remedy the items reported where possible.
  • Prepare for Internationalization FNZ Studio provides a number of features to help you build Solutions that are ready for Internationalization, such as the Label Business Object, the Format Patterns, and Date/Number conversion functions. It is strongly recommended to develop Solutions with Internationalization in mind. Even if the scope of a project does not require translation to other languages for the first release, this need can arise at a later date. If the whole Solution already relies on Labels for the UI texts, adapting to a new language only requires translation effort, no changes to the technical implementation of the Solution are required.

Value Stores

  • Keep Value Stores Small Large Value Stores lead to massive CPU and memory consumption. Make sure the Value Stores of the processes in your solution are smaller than 1MB (max 2MB in exceptional cases). This can be achieved by:
    • Designing a proper data model.
    • Avoiding instantiating unnecessary variables.
    • Saving only what you need for the scope you need it in.
    • Avoiding saving large "integration results" in the Value Stores (such as all search results in a variable).
  • Keep Process Instances Small Process Instances should be small. This is because the FNZ Studio Platform uses them in various places to perform checks on running Processes or show information in FNZ Studio Composition. Make sure the Process Instances of the Processes in your Solution are smaller than 50 KB. This can be achieved by:
    • Storing only needed metadata in Process Instance attributes, meaning that larger related information should be linked instead of copied there.
  • Consider Using Value Store Splitting If you need to collect large quantities of data in your Process, thus leading to larger Value Stores by Process 'nature', evaluate whether to use the Value Store Splitting feature to store collected data.
  • Avoid Unnecessary Value Store Loads Loading more Value Stores than needed lead to massive CPU and memory consumption. Implement the solution in such a way as to avoid frequent unnecessary access to Value Stores, especially in bulk mode (like functions that load all Value Stores at the same time).
    • Don't use the UseValueStoreRO function in your solution. It is only for incident troubleshooting.
    • When implementing Process Message filters, make sure you don't access the Value Store in the filter expression, especially for Processes with lots of Instances (otherwise the Process Engine needs to load all Value Stores belonging to such Instances every five minutes, to check the filter condition). For more information, see Value Store Loading.

Packages

  • Make Packages Loosely Coupled Packages should, whenever possible, be independently deployable. If there are too many dependencies between two Packages (and changes in one Package often requires changes in other Packages), there's something off with your Package structure.
  • Use 'Major.Minor.Patch' Version Numbering Don't invent your own versioning scheme. Make sure the version number changes match the semantics of the scheme.
  • Keep Your Package Interface Stable and Small Have as few Business Objects in your Package interface as possible. Keep each exposed Business Object's own interface (e.g. assigned variables, Screen placeholders) small and stable.
    • Don't use visible Business Objects when the same result can be achieved with Entry Points. Entry Points expose a simpler, more stable interface than visible Business Objects.
    • Don't make a whole Screen replaceable if the same result can be achieved by making a Catalog replaceable (Catalogs expose a simpler, more stable interface than Screens)

Scripting

  • Avoid Hardcoding Large Block of Script Code Hardcoded scripts in Screens, Processes, Rules, and Documents are harder to troubleshoot. These scripts also make the respective Business Objects massive, and lead to lots of code duplication: While Script Functions can be reused, hardcoded scripts in Screens can't. Always put scripts longer than five lines in Script Function Business Objects, or in DataClass Functions.
  • Keep Functions Concise (Factor Your Code) Large functions are harder to maintain. When you change one single line, a whole new version of a large Object is created, consuming more memory, and making it harder to troubleshoot issues. Don’t code any function longer than 50 lines.
  • Don’t Reinvent the Wheel (Especially for Scripting) Out-of-the-box functions are more performant than the equivalent iterative implementation. Before implementing a new ForEach loop to filter Objects manually, search to see if an out-of-the-box function already exists. Collection:Filter and Collection:Map can be more performant versions of what you would implement by hardcoding a loop. Use the Interactive Script Editor's function search feature!
  • Program Defensively Always put "null check" in place for function calls: Don't just assume the function returns a proper result. The USER and USERID functions are the most common cases where null pointers occur, as the functions return null whenever the process is executed in the background. Failing to add a null check leads to hard to fix runtime exceptions and PROD issues.
  • Avoid Accessing Core APIs (No Java!)

The internal APIs are subject to change, making migrations extremely hard. Moreover, many of the internal APIs acquire technical locks that are not intended to be managed by developers in Script Language. Accessing these APIs can lead to production deadlocks and data loss.

  • Avoid Massive Loops FNZ Studio script language is not fit for handling large data sets, as it's an interpreted scripting language. For handling large datasets it’s mandatory to rely on functions that handle the heavy data processing/iterations outside the script interpreter. For example, use the Collection:Filter function, or a SQL query that filters on the backend; don't "Load" and then process in memory. Also avoid implementing looping functions (For, ForEach, While) that iterate on thousands of records. If more than 100 iterations are required, look into alternative implementations.
  • Avoid Cloning Large Objects Cloning an Object creates a copy of the Object in the Value Store, making it significantly larger. In case a copy of an Object is needed, limit the cloning to just the minimum set of properties that must be copied. Cloning a large item can make the size of the Value Store explode. Refrain from using the CLONE function, especially on large Objects.
  • Do Not Use Data Classes to Group Functions Do not use Static Data Class Functions to group Functions into namespaces. Instead, use Script Function Business Objects and Packages for this purpose.
For more information, see Data Class Best Practices
  • Don’t Abuse ERROR Level Logs Polluting the log with tons of ERROR log statements makes it extremely hard to solve production issues, as real errors are mixed up with false positives. Avoid using the LOGGER function at ERROR level as a debug tool, as a massive amount of these ERROR log statements are left in the project after production delivery. Do not use this technique for debugging: Use breakpoints, or at least DEBUG level logs.
Also see Scripting Best Practices for more detailed information on best practices related to scripting.

Processes

  • Make Sure The Process Instances Don’t Run Forever A high number of Process Instances in the system greatly increases memory consumption. Having lots of old running Process Instances also makes it hard to do housekeeping on the platform (because obliteration is not effective, for example). Implement a timer to terminate Process Instances after a certain amount of time to avoid an explosion in the number of running Processes in the system. A suggested maximum duration for a Process Instance is six months.
  • Implement Housekeeping Guidelines Failing to do housekeeping leads to ever-increasing resource (memory and CPU) consumption on a platform over time. Implement cleanup functions that remove all the 'externally stored' Objects at the end of the Process, such as Process messages, cluster files, discussion messages, chat messages, uploaded files, etc.
For more information, see Data Cleanup

Screens

  • Avoid Running Scripts in All Render Phases Executing scripts in all the render phases makes the response time of pages grow significantly, especially because all AJAX Update requests execute such script four times. The Script screen component allows you to configure an always option, that executes a script in all the phases (render, update, validate and process). This option should be avoided.
  • Avoid Custom JavaScript Using custom JavaScript code can lead to runtime issues when upgrading to new platform. Custom JavaScript code in Screens, and especially importing custom JavaScript libraries, must be avoided.
  • Avoid Custom CSS Custom CSS significantly impacts the effort required to adopt a new platform version. Avoid using custom CSS, especially inline CSS styles on components.
  • Avoid HMTL ID Duplicates in Rendered Pages For some Screen Components you need to define the HTML Id property to be able to reference them (e.g. for the execution of Actions). Consider that this value must be unique in any rendered HTML page, since non-unique HTML Ids in a page may cause unexpected behavior in the execution of Screen Actions.
  • Keep Accessibility in Mind when Designing Screens Accessibility means ensuring your FNZ Studio Solution is usable for people with disabilities or special needs (e.g. people with visual impairments). FNZ Studio supports a number of different features that allow you to make your Solutions accessible.

Data Modeling

  • Avoid Duplicate Information Apply normalization to your data model to avoid keeping the same information in multiple places. Failing to apply normalization results in much more complex implementations, which are required to keep the duplicate information consistent in different places of the model.
For more information, see the dedicated section on normalization in the Data Classes Best Practices.
  • Keep Property Count Down Avoid placing all properties into the same huge Data Class. Group them in smaller Data Classes instead and add only the properties which are needed across multiple scopes. Developers often add properties to a Data Class which can be placed in the local Screen or Process scope instead, just to have them available “everywhere”. This causes the data model to grow in size and become more and more difficult to manage.
For more information, see the dedicated section on Data Class properties in the Data Classes Best Practices.
  • Use Inheritance Properly Multiple Inheritance was added to facilitate the implementation of "interface-like" patterns, where a class defines some generic APIs to be adopted by multiple other classes. If multiple inheritance is used for building the actual structure of the data model, it can lead to issues such as function aliasing, difficulties in calling the appropriate constructor and the canonical "diamond problem". Although Data Classes can implement multiple inheritance, it is advisable not to use such feature in most cases.
For more information, see the dedicated section on inheritance in the Data Classes Best Practices.
  • Avoid Introducing Circular References Cyclic data graphs are potentially dangerous on two main grounds. First, circular references are fragile and easy to break. Second, it's harder to test applications that contain circularly-referenced objects. Avoid implementing a solution where circular references are instantiated at runtime, such as when Object A references Object B and Object B references Object A.
For more information, see the recipe on Cyclic References.
  • Use Primitive Types Correctly Only simple Primitive Types such as String, Double, or Boolean are recommended for general use throughout FNZ Studio. Other, more complex Primitive Types can cause issues and their use is not recommended outside of a restricted scope. These complex Primitive Types return a validation warning if used in properties of Data Classes or for variables of Processes, Screens, PDF Outputs, and Data Logic.

Integration

  • Use Asynchronous Integration Whenever Possible Synchronous integration occupies the calling threads for the whole duration of the integration exchange, making the end users perceive the application as "slow". This kind of integration also making the stability and performance of the solution depend on the performance of a backend system, which an FNZ Studio developer usually doesn’t have any control over. Favor asynchronous patterns for integration over synchronous ones by using asynchronous Integration Links.
  • Avoid Long Running Calls Long-running calls keep resources occupied on the calling side. Even if the user isn’t directly impacted, a large number of such calls cause performance issues. Implement timeouts and error handling on calls to external system, including asynchronous ones.
  • Be Careful With Integration Link Versioning Failing to understand Integration Link versioning can lead to runtime issues which are discovered only after the second production release of a project — and these issues become very hard to troubleshoot and fix. The Integration Link versioning mechanism is very peculiar, so you need to consider it carefully before implementing one.
For more information, see Integration Link Versioning.

Configurations

  • Organize Configurations in Server and Content Files Using the two files as suggested above ensures manageable project deployment procedures: Use Server configuration for environment related properties Use Content configuration for common properties across environments
  • Organize Properties Using Prefixes Clearly-worded configuration names help developers understand the purpose of each property without extensive codebase investigations. Configuration properties should be organized with prefixes and be, as much as possible, precise and understandable. For example:
    Copy
    application.aContextIfNeeded.myAwesomeFunctionality.enabled=true/false
  • Add Comments to the Configuration File Similar to the point above, comments in the configuration file help others understand the purpose of each configuration without extensive investigation. You can add notes and comments to the configuration file by prefixing each line with the “#” sign.