ServiceNow Best Practices
ServiceNow Best Practices
ServiceNow Best Practices
Page 1 of 26
1 Document Administration
I.Document Approvals
II.Version Control
Version Author(s) Description of Change Date
01. Braj Bhushan Initial version 11/11/2023
Tamrakar
III.Document Review
Reviewed by Comments Date
IV.Related Documents
The following documents can be used for more information and detail related to the standards described
in this document:
Page 2 of 26
2 Purpose
The purpose of this document is to define the standards that developers should use when working on
ServiceNow instances.
The document is intended to be a “living document” and be regularly updated as more design patterns
are added.
Page 3 of 26
3 Solution check list
Is ServiceNow the right solution for this requirement? It may be better to push this requirement into
another platform or system. Use the following as a guide.
Page 4 of 26
4 Coding requirements and standards
All developers should obtain access to the ServiceNow Developer Portal (free account). Along with
documentation, forums and tutorials, it also provides access to a developer instance that can be used as
for debugging/testing. The recommendations below come directly from the Developer Portal and the
ServiceNow Health Check tool.
4.1.1 Performance
o Large scripts (larger than 500 lines) should be converted into script includes wherever
possible.
o Queries should not exceed out of box transaction quota rules. (sysrule_quota_list.do)
o Recursive Business Rules must not be used.
o Client-side code to fetch data from server should always use Glide AJAX with a callback
function (See section Client Side Coding Best Practice).
o The current.update() method should not be used in an onBefore Business Rule/ Transform
script.
o A module that links to a known large table should have a filter. If the module displays the
records of a large table (>50,000 records), you must add a filter. Unfiltered lists can be very
slow to load, causing performance issues and a poor user experience.
o DOM manipulation is not allowed.
o The default "system" user preference for "rows per page" should be set to 20 or less.
4.1.2 Security
o Integration roles should only inherit the roles for the purpose of that integration. admin, itil,
soap_ecc etc. should not be used for Integration User Roles.
o There must be no hardcoded URLs or passwords in the application code.
o Inbound web services that modify tables should be restricted to appropriate roles.
o Each scoped app/module should have at least one role defined.
o Always use the default ServiceNow APIs such as Glide Form, Glide System, GlideRecord,
GlideRecordSecure and other. Custom code should not be substituted for handling these.
o Test code, logs or any other content, which is not intended for production use, must be
removed from your application.
o Input parameter validation should be server side for sensitive data and secure elements. (All
the validation failures should result in input rejection. Validate for expected data type and
data range.)
o Always escape output in jelly.
o Encrypt Sensitive data with Password2 or encrypted_text type for sensitive data.
o Any inbound and outbound calls to external domains & IPs must be documented.
Page 5 of 26
o Do not send sensitive information over http.
o Protect tables, UI pages, Property pages and other content with proper ACLs and roles.
o Make sure proper ACLs are set for:
▪ Record (Both list and record view)
▪ Client_Callable_Script_Include
▪ Processor
▪ ui_page
o Please use Scripted REST API or web service as integration end points. Don’t use
Processors as integration end points in ServiceNow.
4.2.1 Commenting
• Single line comments start with a double forward slash (//) and can be placed anywhere on the
line.
• Everything after the // and up to the new line is considered a comment. For example:
// Initialize variables
var count = 0;
• Use comments liberally – the goal is for another developer (or future self) be able to quickly view
a section of code and understand its function!
• Block comments start with a forward slash and one asterisk (/*) and end with the reversed
combination (*/).
• Everything between the markers is considered a comment. Adding extra asterisks, like those
Page 6 of 26
shown in this example, can make it easier to locate blocks of comments.
/***
* getRiskEnvironment - determine the environment of the CIs to calculate the risk
*
* @param: chg - GlideRecord of change record
* @return: result - Highest environment value of CIs: 1=production, 2=QA, 3=test, 4
=dev, 0=error
*
***/
• Clearly describe the purpose of functions and all inputs and outputs. For example:
/***
* putUserPref - save a user preference/value pair for this user
* If the preference already exists, check the current value and update
if necessary
*
* @param prefName - name of the user preference
* @param prefVal - string value to save
* @return - None
*
***/
NB: See section on Self-documenting which explains how to add block comments to self document
functions.
4.2.2 Spacing
• Use empty lines and TABs to make code more readable.
• The easier it is to read code, the easier it is to identify and correct issues.
• Empty lines help visually group blocks of code together so the reader can see the logical
organization.
• Spaces on each line help make the items on an individual line easier to read.
• The format code button in the ServiceNow syntax editor toolbar is a useful tool for adjusting
indentation without altering other spacing.
/***
* putUserPref - save a user preference/value pair for this user
* If the preference already exists, check the current value and update
if necessary
*
Page 7 of 26
***/
function putUserPref(){
// Put spaces between parameters and before curly brace
if (childCI == parentCI){
// Put a space after 'if' and around operators ('!=')
return;
}
if (newRelType.get(relType)) {
// Use extra blank lines to help break up chunks of code and improve readability
relTypeID = newRelType.getValue('sys_id');
newRel.initialize();
// Group similar statements together
newRel.setValue('type', relTypeID);
newRel.setValue('child', childCI.getValue('sys_id'));
newRel.setValue('parent', parentCI.getValue('sys_id'));
newRel.insert();
}
}
Complex:
var result = (x == y) ? a : b;
Simple:
var result;
if (x == y) {
result = a;
}
Page 8 of 26
else {
result = b;
}
Page 9 of 26
5 Best Practices
There are two use cases when considering application scope: extending an existing app or creating a
custom app from scratch. If you are extending or modifying an existing global scope application and the
changes are having a high impact on other applications, then leave the change in the global scope.
However, if the changes are going to have less impact to other applications, then the modifications can
be done as a scoped app.
When you are creating a new application from scratch, you should be creating it as a scoped app.
ServiceNow's product strategy is to develop more and more scoped apps to make it easier for customers
to deploy the application and to simplify future upgrades. Also, backing out changes to scoped apps is
much simpler and can be done with a single push of a button.
All Now Platform core services (such as workflow, business rules, and UI policies) that are available in a
global scope are also available in a scoped app. Global artifacts like tables and scripts that are marked as
accessible from a global scope are available in a scoped app. However, there are a few out-of-the-box
APIs that do not work currently in a scoped app, see list
https://developer.servicenow.com/dev.do#!/reference/api/quebec/server/no-namespace
Page 10 of 26
‘grandfathering’ arrangement, but each case may be different and it’s a discussion for the customer to
have with their account manager.
This means the creation of new custom tables should be evaluated to determine the license
implications.
The decision to create custom tables should be raised with the ServiceNow Product Owner/Platform
Architect / Architecture Review Board.
• Client scripts
• Script includes
<<company prefix>><<FunctionDescription>>
Example: AVATUUserHelper
Note: Use “Helper” for script includes that help a particular function e.g. AVATUUserHelper,
TaskHelper etc
• UI Script
<<company prefix>>_<<function_description>>
Example: Avatu_user_functions
• UI Page
Page 11 of 26
<<company prefix>>_<<function_description>>
Example: Avatu_user_login
• UI Macro
<<company prefix>>_<<function_description>>
Example: Avatu_user_form
• Business Rules
• Constants
Example: USER_INFORMATION
• Class
<<company prefix>><<function_description>>
Example: AVATUUserHelper
• Variable
<<functionDescription>>
Example: userName
Page 12 of 26
• Function
<<company prefix>><<function_description>>
Example: AVATUgetUserName
Note: A function is available in the global scope (e.g. used within client scripts) and therefore should
have the company prefix to ensure uniqueness and prevent scope clash.
• Method
<<company prefix>><<method_description>>
Example: getUserName
Note: A method is used within script includes so company prefix is not required (given it is scoped
within the script include class).
• Tables
u_<<description>>
• Fields
<<description>>
Example: Affected contact
• Choice Lists
• System Properties
Example: Avatu.<<function>>.property
Note: Multiple dots can be used to separate sub sections of a property where required.
E.g. Avatu.service_provider.request.email
Avatu.service_provider.request.phone
• Groups
• Roles
<<company prefix>>_<<role_description>>
Example: Avatu_approval_admin
• Notifications
• Email templates
<<company prefix>><<table>><<template>>
Example: Avatu.incident.header.comments.details
• SLAs
<Ticket priority>-<Table/ticket type>-<ReSPond or ReSolVe SLA>-<Business Unit /
Service><space><Description to capture Contract / Region and other details>
Examples:
P1-INC-RSP-OPS UK
Page 14 of 26
P1-INC-RSV-OPS UK
• Knowledge articles
<<Document Type>> - Description
Example:
Customer Service – How to login via ClinPhone
• Scheduled Jobs
<< company prefix>> - Description
Page 15 of 26
// Client side onChange function
function onChange(control, oldValue, newValue, isLoading) {
function updateCampus(response) {
var answer =
response.responseXML.documentElement.getAttribute("answer");
var clearvalue;
if (answer) {
var returneddata = answer.evalJSON(true);
g_form.setValue("campus", returneddata.sys_id,
returneddata.name);
} else {
g_form.setValue("campus", clearvalue);
}
}
Page 16 of 26
// Server side Script Include:
var asu_GetLocationData = Class.create();
asu_GetLocationData.prototype =
Object.extendsObject(AbstractAjaxProcessor, {
getCampus: function () {
var buildingid = this.getParameter('sysparm_buildingid');
var loc = new GlideRecord('cmn_location');
if (loc.get(buildingid)) {
var campus = new GlideRecord('cmn_location');
if (campus.get(loc.parent)){
var json = new JSON();
var results = {
"sys_id":
campus.getValue("sys_id"),
"name": campus.getValue("name")
};
return json.encode(results);
}
} else {
return null;
}
}
});
2. GetReference with callback:
// getReference with a named callback:
g_form.getReference(‘assignment_group’, doGroupCallback);
function doGroupCallback(group){
g_form.setValue(‘email_address’, group.email);
}
Page 17 of 26
// getReference with anonymous callback function:
g_form.getReference(‘assignment_group’, function(group) {
g_form.setValue(‘email_address’, group.email);
}
function doGroupCallback(group){
g_form.setValue(‘email_address’, group.email);
}
Page 18 of 26
5.7.1 Coding Best Practices
1. Use a Run Script Activity at the beginning of the workflow to initialize your workflow scratchpad
variables (including system property gets) to be used downstream.
2. Create a library of functions using a Script Include(s) for reuse.
3. Use code to call Script Includes (Helper) to retrieve data, operate on data or where involved
processing needs to occur.
4. Use a Set Values Activity or Run Script to update the current object. If a Run Script is used do
not use current.update().
5. Handle errors in Try/Catch blocks.
6. Use JSUtil.nil()/.notNil() <string>.nil() in condition statements.
7. When converting a value to a String, use getValue(<field>), but remember that getValue() cannot
dot-walk.
8. If you need workflow debugging, set the system property glide.workflow.log.debug to true and use
workflow.debug during testing.
9. Comment your code
1. Consistency of the workflow, coding best practices and variable usage are the key to reusable
and maintainable code!
2. Your changes to a workflow are not placed into the current update set until you publish.
Page 19 of 26
6 Reusability
6.1.1 Create Small, Modular Components
• Break your code into modular components or specialized functions. Script Include functions are
excellent examples of this technique. Script Includes are essentially libraries of functionality that can
be implemented in other server-side scripts, such as Business Rules, UI actions, and Script Actions.
Some of the benefits of specialized functions include:
o They are easy to create because they are small and simple with limited functionality.
o They are generally simpler and shorter, so it is easy to understand the logic, inputs, and
outputs. This makes it easier for the next person who works with the code to make
modifications.
o They are easier to test. As a related note, when you test the code, be sure to test both valid
and invalid inputs to ensure the script is as robust as possible. As you create small
specialized functions, keep in mind how the small bits fit together in a larger construct. For
example, you may find that doing a query of the same data inside ten separate functions is
not database-friendly, and that a different approach is necessary.
Page 20 of 26
7 Self-documentation
In an effort to improve code understanding and reduce maintenance, a self-documentation tool can be
trialled for the ServiceNow Project. The tool is available from the ServiceNow developer store and
functions similar to industry recognised JSDoc.
/**SNDOC
@name Log
@description Logging function with level determined by parameter. Will log message to ServiceN
ow logs at the specified level.
@param {string} [msg] - Message to be logged into ServiceNow Logs at the Debug level
@param {string} [level] - Level for the message to be logged at. Defaults to Debug level. Leve
ls are 'debug','info','warn' or 'error'
@param {string} [id] - Unique Identifier for tracking series of logs.
@example
*/
Page 21 of 26
• Documentation is live, there is no need to run a specific process to update the
documentation, just open up a portal to see the latest documentation
• Documentation relies entirely on the comments that you create
Page 22 of 26
8 Making updates to OOTB
The ServiceNow platform delivers baseline functionality for licensed applications which can be
configured from OOTB (out of the box) with no customisations to get the customer up and running very
quickly, once the customer’s data has been loaded. However, there is usually a requirement for
ServiceNow consultants to review the customer’s business processes and perform some additional
development to tailor the platform to suit the customer’s business needs, which means changing OOTB
objects which impacts on future upgrades.
“Customizations should be made to baseline objects where necessary, so that conflict resolution and
decision making can be appropriately recorded in the updates. Hidden customizations may cause
administrators to overlook updates in future assessments in case reverts or merges are necessary.”
Refer KB0553407
Additionally, invalid updates such as this can impact on the upgrade process whereby “touched” objects
are reported as an upgrade conflict, even if you manually removed your changes but left the update
record in an update set which was moved through the landscape into PROD.
Page 23 of 26
After performing manual updates in your code or relevant fields to restore the changes back to original,
this flag does not get ‘reset’.
We need to navigate to the Application File for the object in question to reveal all recorded versions.
From the customer update record, the navigation path is - Show Related Record link (under Related
Links), then Context Menu->Show File Properties
Click on the linked Name field where the source is the original version (in the example above it’s the
record where the source is “System Upgrades…”)
Now we are on the Update Version form. From here, click the “Revert to this version” link (under
Related Links)
The version list has now been updated to reflect that the current version is now the original version as it
has been restored.
Page 24 of 26
The ‘Replace on upgrade’ flag which was showing as empty earlier, has now been updated to reflect that
this object should be replaced in an upgrade.
We are not done until all customer updates for the object in question are removed from in-flight update
sets. The revert action which we just performed is not recorded as a customer update.
As a side note, the revert operation might be represented differently if the object was installed as a
store application, in which case you access the ‘Revert to Store App’ link under Related Links.
Page 25 of 26
9 General Tips
• Be wary that ServiceNow server side uses Mozilla Rhino and supports up to ES5 whereas client
side (browser) javascript uses ES6. This means there could be functions available in the browser
(ES6) that are not available on the server (ES5). If unsure if a server side javascript function is
available – test in a background script.
• When workflows are not required to be run in a server-side script, use setWorkflow(false)
• When glide record is not required to have last updated/created changed via a script, use
autoSysFields(false)
• The Glide Element function getRefRecord can be a more efficient way to get a referenced field
rather than querying for it.
• The Glide System function nil() can be used to test if an object is null, undefined or empty.
• The Glide Record function of getValue and setValue should always be used for getting and
setting values.
Server-side GlideRecord objects contain an element for each field on the record. These elements
are not primitive values (strings, numbers, Booleans), but Objects. The type of object is
GlideElement, and the usual GlideElement API is available. However, since JavaScript is a
loosely typed language, you can reassign a variable of one data-type (such as an Object) to
another type (such as a string). Thus, setting a GlideRecord (gr) element's value to a string will
overwrite the reference to the GlideElement object itself. This bad-practice example would look
like:
gr.short_description = 'New short desc.';
This would replace the GlideElement object with a string. This is not good.
Technically, Rhino (the server-side implementation of JavaScript that ServiceNow uses) handles
this on the server so we can't overwrite the GlideElement object with a string like this, but it's still
best practice not to write our code like this.
• The ServiceNow Developer Portal has an excellent list of API reference functions.
Page 26 of 26