UPDATE SETS BEST PRACTICES
According to ServiceNow, update sets can be defined as “A grouping of customizations into a single package that can easily be promoted to other instances.” Customizations include – Tables, Forms, Fields, Business Rules, Clients Scripts, Views, etc….
Use update sets:
- To make change you intend to apply to other instances
- To ensure instances are in sync.
No need to use an update set when:
- You are moving data from one source to another (use data extracts/import sets.)
- All changes when not within a custom update set, will be tracked in the Default update set. ServiceNow says that “This way we can see can see what you have changed” and use this for problem resolution as your change is at least tracked “somewhere”.
- Update sets should have relevant naming conventions like [Team Name][Sprint Number][Application Name][Initials][Date][Version] (Choice of your own to distinguish your team’s update set from other teams pretty quick)
Suggested Naming convention:
- For immediate fixes, suggested naming is update set name + FIX ( like **********-FIX). Fixes would be for the same day committed applications.
- For Emergency Change; update set name + EChg (like ************-EChg). Emergency Changes would be fixes for issues found later. This would be mostly for Rollbacks.
BUSINESS RULES BEST PRACTICES
- Know When to Run/Use Business Rules
- Display – Run just before the form is loaded
- Before – Generally used for manipulating the current record
- After – Best used for updating related, not current, objects. Generally used for GlideRecord queries, managing related tables/ﬁelds.
- Async – Processed a short time after the current action has finished depending on system resources
- Avoid updating other records in a Before Business Rule:
“Before” “Insert/Update” Business Rules are nice because they allow us to modify the current (record) Object just before the Database operation is executed. However, you can hinder system performance if you are unnecessarily updating other records during this script. This is because an “insert/update” action on another table will likely invoke any applicable Business Rules configured on that table as well, and thus delay the current action being performed. If you need to trigger an action on another table record it is better to do this in an “After or Async” Business Rule, but knowing which one to use will depend on your circumstances. “After” Business Rules are triggered immediately after the current action has occurred (e.g. when a database record has been updated), and they still have access to the previous Object (as well as the changes() method etc.). Async Business Rules on the other hand, work like a once-off scheduled job, and are processed a short time after the current action has finished depending on system resources. For interactive sessions (e.g. User clicks a Save UI Action), the Before and After Business Rules will run before the User sees the Form reload. Long running Before/After Business Rules will increase this duration. Async Business Rules on the other hand, will not affect this duration as they are queued for the system to run in the background.
- Use Conditions in Business Rules:
When a database transaction occurs, all of the applicable Business Rules will be executed in the following sequence:
- [User opens a List]
- [User opens a Form]
- [User inserts/updates/deletes a record]
- Async (scheduled)
Within each of the above groups, Business Rules are further sequenced by their Order number. Because it’s possible (and likely) that multiple Business Rules (of each type) have been defined, the system will effectively combine all of them into one big long script for processing (where the order is determined by the Order field on each rule). If no Condition is configured on these rules, all of them will be executed every time. However, if you define a condition on the Condition field (or the Condition Builder), the system will first evaluate these conditions and only include the script contents/actions if the condition evaluates to true. This will improve system performance, as only the applicable Business Rules will be triggered. Note that the Condition field has a limited character length, so if your condition is quite long you will need to move the logic to a Script Include function and call it from the Condition field instead.
- Keep Code in enclosed Functions
- When code is not enclosed in a function, variables and other objects are available to all other server-side scripts. This availability can lead to unexpected consequences that are difficult to troubleshoot.
- Prevent Recursive Business Rules
- Do not use current.update() in a Business Rule script. The update() method triggers Business Rules to run on the same table for insert and update operations, potentially leading to a Business Rule calling itself over and over. When a recursive Business Rule is detected, ServiceNow stops it and logs the error in the system log. However, this behavior may cause system performance issues and is never necessary.
- Use Script Includes Instead of Global Business Rules
- To avoid additional processing load, which will affect the system performance use Script Includes, which can be used to serve the same purposes, however they are only loaded if their function call is detected. The most common example of this is a Reference Qualifier script. Since this script might only be needed on a couple of table fields in the system, it is far more efficient to have this script loaded as required instead of on EVERY table!
- Use Business Rules to Double-Check Critical Input
- Data may change (by the time the user fills in the fields, and the time the user submits the form), this mismatch can cause a conflict or error. Business Rules are an effective way to double-check critical input.
Need More Explanation:
CLIENT SCRIPTS BEST PRACTICES
To avoid this, here are few Best Practice recommendations:
- Enclose code in functions
- Run Only Necessary Client Scripts
- Client Scripts have no Condition field. This means onLoad() and onChange() scripts run in their entirety every time the appropriate form is loaded. To avoid running time-consuming scripts unnecessarily, make sure Client Scripts perform only necessary tasks
- Use UI Policies instead of Client Scripts
- Avoid global Client Scripts
- Client Scripts configured on the Global Table will be loaded on every single table form in the system. This can seriously impact system performance and therefore should be avoided.
- Run server calls as late as possible within the script. Use Asynchronous calls via GlideAjax, use g_scratchpad and GlideAjax to minimize server calls (instead of using g_form.GetReference() and GlideRecord)
- Using Asynchronous AJAX calls (e.g. using Callback functions) will avoid freezing the web browser while the data transfer occurs, and results in a much nicer user experience.
- Avoid using DOM (manipulating elements via the Document Object Model)
- It can cause a maintainability issue when browsers are updated. Set the Display Value as well as the Value on Reference fields.
- Set the Display Value as well as the Value on Reference fields (Like sys_id and also the record value[name])
Need More Explanation:
SERVER SCRIPT BEST PRACTICES
- Use GlideAggregate instead of getRowCount()
At times we’ve all needed to check how many records were returned by a specific query, and we probably did this by using a function from the GlideRecord Class called getRowCount(). This will work however, under-the-hood it is not the most efficient means of obtaining such a count. This is because the GlideRecord Class will retrieve the values of each record as well as the number of records returned. It is therefore recommended that we use the GlideAggregate Class instead. This Class operates with some similar functions and parameters as the GlideRecord Class, however its purpose is not to retrieve all of the record data but simply to provide statistics based on the records returned by a query.
- Avoid hardcoding values by using System Properties and Messages
As a Developer, it is very easy to hardcode values into scripts that we write. Often this might be to assign an approval or task to a specific group, or perhaps display an onscreen error message. In doing so we make it difficult to maintain and make changes to these values, since they could be buried deep within our code and changing them will often require an Update Set migration. In ServiceNow, a common practice is to hardcode sys_id values into scripts so our query will always find the same record, but this is against the ServiceNow Best Practice, and can easily be avoided by simply using a System Property. Why use a System Property you ask? Well if you store the sys_id in a System Property, an Administrator (or anyone with access to the specific System Property via Roles) can easily update the value later, and not necessarily via the Update Set migration process. System Properties allow us to categorize and describe the values we store in them. I recommend adding your System Properties to a Category (they can belong to more than one), and then adding a menu module (link) to display them. This will allow Administrators to easily find and maintain them. Likewise, if you need to provide specific error or information messages to the user, consider storing these in the Message table. These values can be easily retrieved on both the Client and Server using the standard APIs, and have the added advantage of being mapped against a specific language. This allows us to customize the messages for multi-language configurations. Remember, the key is to avoid hardcoding values, as it won’t always be us making the changes, so please think about the next person working on your code!
CODING BEST PRACTICES
- Use consistent styling:
First and foremost, code should be indented consistently. Much like reading a book, developers learn to read (and write) code by knowing how the control (program flow) will branch based on conditions or loops etc. If code is not indented consistently, it becomes very difficult to understand what the code is doing, and therefore how to successfully modify it. By using indentation, you can avoid having to count how many open or close curly braces there are, when identifying which IF or FOR block the current line of code falls within. Trust me, it’s easier! And, considering that ServiceNow offers a “Format Code” function as part of the Script field toolbar, you really have no excuses.
- Know your data types:
- Avoid non-descriptive names or Variables and function names:
Avoid using Variables with extremely short names, as it becomes more difficult to understand the purpose of the variable throughout the code. Exceptions to this rule include using single letter Integers for iterating through loops (e.g. i), although it is still advisable to describe these variables with some detail (especially when you have multiple loops in the same function). In addition to this, you should also avoid using extremely long names. Try to find some middle ground and give your Variables a name with purpose.
- Use Objects:
- Keep it simple:
Some developers will often try to condense their code into a few lines as possible, believing that this is more efficient, and this is certainly the case in some programming languages. However, in my experience using the ServiceNow platform, a few extra lines of code used to define additional Variables won’t make a huge difference. For example, often times you will need to evaluate a number of conditions before branching your code based on the outcome. In ServiceNow, this often involves dot walking from the “current” object or calling functions from the API to return a value. If you add all of these into the condition of an IF block, it can become messy and difficult to understand, let alone debug. For this reason I will often simplify the overall condition by first retrieving each component as a true/false value, and storing them as Boolean Variables. Then I use these Variables in the condition block. If you need to debug an issue, simply add some log statements to display the value of each of these components. For complex logical operations, it would be better to move the logical processing to its own function, and have that function return the overall result to your IF block.
- Error trapping and preventing crashes:
There are many reasons why your code can fail/crash, or produce undesired results. A major cause of this is when the developer has not considered all of the possible scenarios from a logical and technical point of view. A simple example is processing input from a user. If the code is expecting a number but the user enters some text, how will it react? If the developer assumed the value would always be a number, and therefore did not allow for any exceptions, the code will likely fail. Simply put, a piece of code can be modified to allow exception cases to exist without crashing the code. This is generally referred to as “error trapping”, where the goal is to handle any exception cases without causing the code to crash. A simple solution to the above scenario would be to detect the undesirable input value and report an error message back to the user, rather than letting the code continue running and potentially fail. This simple principle of checking whether a value is within the nominal range, or the expected data type, can make all the difference. This also applies to values returned by functions called by your own code.
Last but not least, you should always comment your code. Comments not only tell other developers what your code does (or at least what it was intended to do), they also tell you what your code does. Unless you have an exceptional memory, remembering exactly what each line of code does in a function you wrote 12 months earlier will likely be a challenge. As a wise developer once said, be kind to your future self and comment your code. This is not to say that every line of code requires a comment, but I do suggest commenting the following:
- Groups of Variables or Objects (a single line describing what each group is for)
- Functions (describe in a few lines the purpose of the function and what arguments or return values it uses)
- IF blocks or FOR loops (describe the purpose of each)