
ServiceNow Script Includes Explained: Reusable Server-Side Logic
As ServiceNow applications grow, duplicated server-side logic becomes one of the easiest maintenance problems to create.
A Business Rule needs to calculate something.
A UI Action needs the same calculation.
Later, a Scheduled Script Execution needs it too.
Then a Scripted REST API needs almost the same logic.
The quickest solution is often to copy and paste the code.
That works initially, but now the application has several implementations of the same business rule.
When the requirement changes, every copy needs to be found and updated.
A Script Include gives that reusable logic one home.
Instead of duplicating the implementation, other server-side scripts call the Script Include.
In this guide, we'll look at:
- what Script Includes are
- when to use them
- how to create reusable classes and methods
- how they work with GlideRecord
- how Business Rules should call them
- client-callable Script Includes and GlideAjax
- application scope and accessibility
- common design mistakes
- practical patterns for maintainable ServiceNow applications
What Is a Script Include?
A Script Include is reusable JavaScript stored on the ServiceNow server.
It can contain:
- functions
- classes
- helper methods
- reusable queries
- business calculations
- validation logic
- data transformation
- integration helpers
Other scripts can call that logic instead of implementing it again.
For example, imagine several parts of an application need to determine whether an Incident should be considered critical.
Instead of duplicating that rule, we can create one Script Include.
1var IncidentUtils =2 Class.create()3
4IncidentUtils.prototype = {5 initialize:6 function() {},7
8 isCritical:9 function(incidentGR) {10 return (11 incidentGR.getValue(12 'priority',13 ) === '1'14 )15 },16
17 type:18 'IncidentUtils',19}A server-side caller can then use:
1var utils =2 new IncidentUtils()3
4if (5 utils.isCritical(6 current,7 )8) {9 gs.info(10 'Critical incident'11 )12}Now the definition of "critical" exists in one place.
Why Script Includes Matter
Without reusable server-side logic, applications tend to develop code like this:
Business Rule
1if (2 current.priority == 1 &&3 current.active == true4) {5 // logic6}UI Action
1if (2 current.priority == 1 &&3 current.active == true4) {5 // almost the same logic6}Scheduled Script
1if (2 incidentGR.priority == 1 &&3 incidentGR.active == true4) {5 // another copy6}Now imagine the business rule changes.
Critical incidents must also belong to a particular service.
Every implementation needs updating.
If one copy is missed, the application behaves inconsistently.
A Script Include lets every caller use the same implementation.
A Useful Mental Model
Think of the relationship like this:
Business Rule
Decides when reusable logic should run.
Script Include
Contains the reusable logic itself.
For example:
When an Incident changes to Critical, run our escalation calculation.
The Business Rule detects the record lifecycle event.
The Script Include performs the reusable calculation.
This keeps Business Rules smaller and easier to understand.
Script Includes Are Server-Side by Default
A normal Script Include runs on the ServiceNow server.
That means it can use server-side APIs such as:
1GlideRecordand:
1gsFor example:
1var IncidentUtils =2 Class.create()3
4IncidentUtils.prototype = {5 initialize:6 function() {},7
8 getActiveCriticalCount:9 function() {10 var incidentGA =11 new GlideAggregate(12 'incident',13 )14
15 incidentGA.addQuery(16 'active',17 true,18 )19
20 incidentGA.addQuery(21 'priority',22 '1',23 )24
25 incidentGA.addAggregate(26 'COUNT',27 )28
29 incidentGA.query()30
31 if (32 incidentGA.next()33 ) {34 return parseInt(35 incidentGA.getAggregate(36 'COUNT',37 ),38 10,39 )40 }41
42 return 043 },44
45 type:46 'IncidentUtils',47}The caller does not need to know how the count is calculated.
It simply asks:
1var utils =2 new IncidentUtils()3
4var count =5 utils.getActiveCriticalCount()A Basic Class-Based Script Include
A common Script Include structure looks like:
1var ExampleUtils =2 Class.create()3
4ExampleUtils.prototype = {5 initialize:6 function() {},7
8 doSomething:9 function() {10 return 'result'11 },12
13 type:14 'ExampleUtils',15}There are three important pieces.
Class.create()
Creates the class structure.
prototype
Defines the methods available on instances.
type
Identifies the class.
For a normal class-style Script Include, keep the Script Include name and class name consistent.
Creating an Instance
Once the Script Include exists, another server script can create it using:
1var utils =2 new ExampleUtils()Then call:
1var result =2 utils.doSomething()This works from server-side contexts that can access the Script Include.
Pass Data Into Methods Explicitly
Reusable methods are easier to understand when their inputs are clear.
For example:
1calculateScore:2 function(3 impact,4 urgency,5 ) {6 return (7 parseInt(8 impact,9 10,10 ) *11 parseInt(12 urgency,13 10,14 )15 )16 }Then:
1var utils =2 new IncidentUtils()3
4var score =5 utils.calculateScore(6 current.getValue(7 'impact',8 ),9 current.getValue(10 'urgency',11 ),12 )Anyone reading the call can see what the method requires.
Avoid Hidden Dependence on current
Inside a Business Rule, current is available automatically.
That can tempt developers to write reusable logic that assumes current exists everywhere.
For example, avoid designing a Script Include method around an invisible dependency such as:
1doSomething:2 function() {3 return current.priority4 }Now the method only works in contexts where current happens to exist.
A more reusable method accepts what it needs:
1isCritical:2 function(recordGR) {3 return (4 recordGR.getValue(5 'priority',6 ) === '1'7 )8 }Then the caller decides which record to provide.
Business Rules Should Stay Small
Consider a Business Rule containing:
- several GlideRecord queries
- complex calculations
- formatting logic
- validation
- related-record processing
- integration logic
The Business Rule may technically work, but its responsibility becomes difficult to understand.
A cleaner Business Rule might look like:
1(function executeRule(2 current,3 previous,4) {5 var service =6 new IncidentService()7
8 service.handleEscalation(9 current,10 )11})(current, previous)Now the Business Rule communicates:
When this record condition occurs, handle escalation.
The reusable logic lives elsewhere.
Don't Move Everything Out of Business Rules
The goal is not to make every Business Rule contain exactly one line.
Simple logic can remain simple.
For example:
1current.u_last_processed =2 gs.nowDateTime()does not necessarily need a separate Script Include.
Create reusable abstractions when they provide real value.
Typical reasons include:
- logic is used in several places
- the implementation is complex
- it deserves independent testing
- the Business Rule is becoming difficult to read
- the logic represents a reusable application service
Script Includes Can Contain GlideRecord Queries
Suppose several scripts need to retrieve active critical Incidents.
Instead of repeating:
1var incidentGR =2 new GlideRecord('incident')3
4incidentGR.addQuery(5 'active',6 true,7)8
9incidentGR.addQuery(10 'priority',11 '1',12)13
14incidentGR.query()we can create a reusable method.
1var IncidentUtils =2 Class.create()3
4IncidentUtils.prototype = {5 initialize:6 function() {},7
8 getActiveCritical:9 function() {10 var results = []11
12 var incidentGR =13 new GlideRecord(14 'incident',15 )16
17 incidentGR.addQuery(18 'active',19 true,20 )21
22 incidentGR.addQuery(23 'priority',24 '1',25 )26
27 incidentGR.query()28
29 while (30 incidentGR.next()31 ) {32 results.push(33 incidentGR34 .getUniqueValue()35 )36 }37
38 return results39 },40
41 type:42 'IncidentUtils',43}The callers no longer need to know the query implementation.
Return Useful Data
Think carefully about what your Script Include should return.
Possible return types include:
- boolean
- string
- number
- array
- object
sys_id- GlideRecord
For example:
1isCritical:2 function(incidentGR) {3 return (4 incidentGR.getValue(5 'priority',6 ) === '1'7 )8 }returns a boolean.
That makes the caller simple:
1if (2 utils.isCritical(3 current,4 )5) {6 // ...7}Prefer Predictable Return Types
Avoid a method that sometimes returns:
- a GlideRecord
false- an empty string
- an object
depending on what happened.
That forces every caller to understand several unrelated cases.
Prefer predictable contracts.
For example:
1getUserEmail:2 function(userSysId) {3 if (!userSysId) {4 return ''5 }6
7 var userGR =8 new GlideRecord(9 'sys_user',10 )11
12 if (13 !userGR.get(14 userSysId,15 )16 ) {17 return ''18 }19
20 return (21 userGR.getValue(22 'email',23 ) || ''24 )25 }The caller knows the method always returns a string.
Validate Method Inputs
Reusable methods may eventually be called from places you did not originally expect.
Validate important inputs.
For example:
1getUserEmail:2 function(userSysId) {3 if (!userSysId) {4 return ''5 }6
7 // continue8 }For more complex operations, validate:
- required IDs
- expected values
- record existence
- permissions where relevant
- input format
A reusable method should not assume every caller is perfect.
Keep Methods Focused
A method called:
1getUserEmail()should probably retrieve an email address.
It should not also:
- update the user
- create an Incident
- send an event
- close another task
Method names should accurately describe their responsibility.
This makes Script Includes easier to trust and reuse.
Avoid the Giant Utils Class
One of the easiest Script Include problems to create is:
1GlobalUtilswith 80 unrelated methods.
It might eventually contain:
- Incident calculations
- user queries
- date formatting
- group membership
- asset logic
- approval logic
- integrations
Technically, the code is reusable.
Architecturally, it has become a dumping ground.
Prefer focused Script Includes.
For example:
IncidentUtilsUserUtilsAssignmentServiceApprovalServiceAssetUtils
The right names depend on the application.
Utility vs Service Style
Not every Script Include has to be called Utils.
A useful distinction is:
Utility
Small reusable helpers.
For example:
1DateUtils2StringUtils3IncidentUtilsService
Represents a larger application responsibility.
For example:
1IncidentEscalationService2AssignmentService3ApprovalServiceA service may coordinate several queries and business rules around one feature.
Naming the Script Include after its responsibility makes the architecture clearer.
Example: Assignment Service
Imagine assignment logic is used from:
- a Business Rule
- a UI Action
- a Scripted REST API
Instead of duplicating the logic, create:
1var AssignmentService =2 Class.create()3
4AssignmentService.prototype = {5 initialize:6 function() {},7
8 assignIncident:9 function(10 incidentGR,11 groupSysId,12 ) {13 if (14 !incidentGR ||15 !incidentGR.isValidRecord()16 ) {17 return false18 }19
20 if (!groupSysId) {21 return false22 }23
24 incidentGR.setValue(25 'assignment_group',26 groupSysId,27 )28
29 incidentGR.update()30
31 return true32 },33
34 type:35 'AssignmentService',36}Now several server-side entry points can use the same assignment implementation.
Be Intentional About Who Performs update()
Reusable logic should have a clear contract around database persistence.
Compare these two method designs.
Method changes the record only
1applyDefaults:2 function(recordGR) {3 recordGR.setValue(4 'priority',5 '3',6 )7 }The caller remains responsible for saving it.
Method performs the operation
1assignIncident:2 function(3 recordGR,4 groupSysId,5 ) {6 recordGR.setValue(7 'assignment_group',8 groupSysId,9 )10
11 return recordGR.update()12 }Both designs can be valid.
The important thing is that callers understand whether the method:
- only modifies an object
- or performs a database operation
Hidden database writes can make reusable methods dangerous.
Name Methods to Reveal Side Effects
A method called:
1getAssignmentGroup()sounds read-only.
It should not silently update records.
A method called:
1assignToGroup()clearly suggests a state change.
Good naming makes side effects easier to understand before reading the implementation.
Script Includes Can Reduce Repeated GlideRecord Code
Suppose five different scripts need a user record.
A reusable method might be:
1getUser:2 function(userSysId) {3 if (!userSysId) {4 return null5 }6
7 var userGR =8 new GlideRecord(9 'sys_user',10 )11
12 if (13 !userGR.get(14 userSysId,15 )16 ) {17 return null18 }19
20 return userGR21 }This can reduce repeated retrieval logic.
But do not create a wrapper around every single GlideRecord operation just because you can.
A reusable method should represent something meaningful to the application.
Reuse Business Meaning, Not Just Syntax
This method:
1queryTable:2 function(3 table,4 field,5 value,6 ) {7 // generic query8 }may not add much value beyond GlideRecord itself.
But:
1getActiveApprovers:2 function(requestSysId) {3 // application-specific logic4 }expresses meaningful business behavior.
The best reusable methods often hide business complexity, not merely JavaScript syntax.
Keep Database Filtering Inside the Query
The same GlideRecord best practices apply inside Script Includes.
Prefer:
1incidentGR.addQuery(2 'active',3 true,4)5
6incidentGR.addQuery(7 'priority',8 '1',9)rather than retrieving every Incident and filtering in JavaScript.
A Script Include does not make an inefficient query efficient.
Avoid Queries Inside Large Loops
Script Includes can make expensive logic look harmless.
For example:
1getManager:2 function(userSysId) {3 var userGR =4 new GlideRecord(5 'sys_user',6 )7
8 if (9 userGR.get(10 userSysId,11 )12 ) {13 return userGR.getValue(14 'manager',15 )16 }17
18 return ''19 }This may be fine when called once.
If another script calls it 5,000 times inside a loop, it could generate thousands of queries.
Reusable methods still need performance awareness.
Think About the Caller
When creating reusable code, ask:
How often will this method be called?
A function used once per form submission is different from a function called for every record in a 100,000-record batch.
Consider:
- query count
- result size
- caching opportunities
- repeated reference lookups
- transaction timing
Abstraction does not remove performance costs.
Classless or On-Demand Script Includes
Not every Script Include needs to define a class.
ServiceNow also supports simple reusable functions.
Conceptually, an on-demand Script Include might expose one function whose name matches the Script Include.
For example:
1function calculateSomething(2 value,3) {4 return (5 parseInt(6 value,7 10,8 ) * 29 )10}This can be useful for a small reusable function.
For larger groups of related methods, a class-based Script Include is usually easier to organize.
Class-Based Script Includes Scale Better for Related Methods
Imagine an Incident utility needs:
isCritical()getOpenChildCount()getAssignmentGroup()
Keeping related methods together can make sense.
For example:
1var IncidentUtils =2 Class.create()3
4IncidentUtils.prototype = {5 initialize:6 function() {},7
8 isCritical:9 function(incidentGR) {10 return (11 incidentGR.getValue(12 'priority',13 ) === '1'14 )15 },16
17 getOpenChildCount:18 function(19 incidentSysId,20 ) {21 // query here22 },23
24 type:25 'IncidentUtils',26}The class provides a clear namespace for related functionality.
Script Includes and Application Scope
ServiceNow applications can run in different scopes.
A Script Include created inside a scoped application belongs to that application scope.
When deciding whether other scopes should be able to use it, consider:
- who actually needs the API
- whether cross-scope use is intended
- whether the methods expose sensitive operations
- whether the implementation should remain internal
Do not expose reusable logic more broadly than necessary.
Treat Script Includes Like Application APIs
Once several parts of your application depend on a Script Include, its methods effectively become an internal API.
For example:
1service.getAssignment(2 recordSysId,3)Other code now depends on:
- the method name
- parameters
- return type
- behavior
Changing that contract can break multiple callers.
That means Script Includes deserve thoughtful method design.
Keep Public Methods Stable
Suppose several callers use:
1getCriticalIncidents()Changing it suddenly to require several new parameters may break those callers.
As Script Includes become widely used, treat changes carefully.
For larger applications, think about:
- backward compatibility
- method contracts
- input validation
- predictable returns
This is ordinary API design applied inside ServiceNow.
Private Helper Methods
A Script Include may contain helper methods that exist only to support its public methods.
For example:
1var IncidentService =2 Class.create()3
4IncidentService.prototype = {5 initialize:6 function() {},7
8 calculatePriority:9 function(10 impact,11 urgency,12 ) {13 var score =14 this._calculateScore(15 impact,16 urgency,17 )18
19 return this20 ._priorityFromScore(21 score,22 )23 },24
25 _calculateScore:26 function(27 impact,28 urgency,29 ) {30 return (31 parseInt(32 impact,33 10,34 ) *35 parseInt(36 urgency,37 10,38 )39 )40 },41
42 _priorityFromScore:43 function(score) {44 if (score <= 1) {45 return '1'46 }47
48 return '3'49 },50
51 type:52 'IncidentService',53}A naming convention such as a leading underscore can communicate that a method is intended as an internal helper.
It is still JavaScript rather than true language-level privacy, but the intention is clearer.
Script Includes Are Easier to Test Than Duplicated Logic
Suppose a priority calculation lives directly inside several Business Rules.
Testing it requires exercising each Business Rule context.
If the calculation lives in:
1IncidentService.calculatePriority()you can test that method independently with known inputs.
For example:
1var service =2 new IncidentService()3
4gs.info(5 service.calculatePriority(6 '1',7 '1',8 )9)This makes development and troubleshooting easier.
Background Scripts Are Useful for Testing
In a development instance, Background Scripts can be useful for quickly exercising server-side Script Includes.
For example:
1var utils =2 new IncidentUtils()3
4var result =5 utils.getActiveCriticalCount()6
7gs.info(8 'Result: ' +9 result10)This lets you verify the reusable logic separately from its Business Rule or other caller.
Use appropriate care when testing code that performs updates or deletes.
Log Meaningful Information During Development
When troubleshooting a reusable method, logs can help reveal:
- which inputs were provided
- which path was taken
- how many records matched
- what result was returned
For example:
1gs.debug(2 'IncidentService: processing ' +3 incidentSysId4)Avoid leaving excessive noisy logging in heavily executed production paths.
Throwing Errors vs Returning Failure
Reusable methods need a consistent error strategy.
A validation-style method may simply return:
1falseA lookup may return:
1nullor:
1''A more serious unexpected failure may deserve explicit error handling.
The right approach depends on the method.
What matters is consistency.
Callers should know what failure looks like.
Don't Swallow Every Error
Avoid patterns that silently hide unexpected failures.
For example:
1try {2 // everything3} catch (e) {4 return false5}Now a programming error and a normal validation failure look identical.
When errors matter, log or handle enough context to make them diagnosable.
Client-Callable Script Includes
So far, we've discussed server-to-server reuse.
But sometimes a Client Script needs information that must be calculated on the server.
That is where a client-callable Script Include can be used with GlideAjax.
The flow is:
Client Script
→ GlideAjax
→ Client-callable Script Include
→ server-side processing
→ result returned to the browser
This gives the browser access to a controlled server-side operation without moving server logic into the Client Script.
Why Use GlideAjax?
Imagine a Client Script needs to know a user's manager.
The browser does not need an entire user record.
It needs one answer.
Instead of pulling unnecessary data to the client, a GlideAjax call can ask a Script Include:
What is this user's manager?
The Script Include performs the server-side work and returns the result.
Client-Callable Script Includes Need Explicit Configuration
Normal Script Includes are not automatically intended for direct client calls.
For GlideAjax use, configure the Script Include appropriately as client callable.
This makes the server-side endpoint available to client-side callers according to the platform's access rules.
Do not make every Script Include client callable.
Expose only what actually needs to be reached from the client.
A GlideAjax Script Include
A common client-callable structure extends AbstractAjaxProcessor.
For example:
1var UserAjax =2 Class.create()3
4UserAjax.prototype =5 Object.extendsObject(6 global.AbstractAjaxProcessor,7 {8 getManagerName:9 function() {10 var userSysId =11 this.getParameter(12 'sysparm_user_id',13 )14
15 if (!userSysId) {16 return ''17 }18
19 var userGR =20 new GlideRecord(21 'sys_user',22 )23
24 if (25 !userGR.get(26 userSysId,27 )28 ) {29 return ''30 }31
32 return userGR33 .manager34 .getDisplayValue()35 },36
37 type:38 'UserAjax',39 },40 )The method reads parameters sent by the client and returns the requested result.
Calling It From a Client Script
A Client Script could call that Script Include with:
1var ga =2 new GlideAjax(3 'UserAjax',4 )5
6ga.addParam(7 'sysparm_name',8 'getManagerName',9)10
11ga.addParam(12 'sysparm_user_id',13 g_form.getValue(14 'caller_id',15 ),16)17
18ga.getXMLAnswer(19 function(answer) {20 if (answer) {21 g_form.setValue(22 'u_manager_name',23 answer,24 )25 }26 },27)The important architectural split is:
Client Script
Handles the form.
Script Include
Handles the server-side data retrieval.
Keep GlideAjax Asynchronous
Client-server communication introduces latency.
Use asynchronous patterns so the browser is not unnecessarily blocked while waiting for the server.
The client sends the request.
Other browser activity can continue.
When the response arrives, the callback processes it.
This provides a much better user experience than blocking client execution while waiting for the server.
Return Only What the Client Needs
Suppose the browser needs:
- manager name
Do not return an entire large record structure.
Return the smallest useful result.
For example:
1return userGR2 .manager3 .getDisplayValue()This reduces:
- network payload
- parsing
- unnecessary data exposure
- client-side complexity
Returning Multiple Values
Sometimes the client genuinely needs several related values.
One option is to return JSON.
For example:
1var result = {2 name:3 userGR.getDisplayValue(),4
5 email:6 userGR.getValue(7 'email',8 ),9
10 manager:11 userGR12 .manager13 .getDisplayValue(),14}15
16return JSON.stringify(17 result,18)Then the client can parse the response.
Only include fields the browser actually needs.
Validate Client-Supplied Parameters
A GlideAjax method receives values from the client.
Do not automatically trust those inputs just because the request came from your own Client Script.
Validate:
- required parameters
- referenced records
- allowed values
- permissions where appropriate
The browser is not an authoritative security boundary.
Client Callable Does Not Mean Public Business Authority
A client-callable Script Include should not blindly perform privileged operations simply because the client requested them.
For example:
Delete any Incident whose sys_id I send.
would be a dangerous server API without proper authorization and validation.
Client-callable methods should be designed with the same care as other server endpoints.
Don't Make One Ajax Script Include Do Everything
Another common mistake is creating:
1AjaxUtilswith dozens of unrelated client-callable methods.
As the application grows, it becomes difficult to:
- understand
- secure
- test
- maintain
Prefer endpoints grouped around meaningful responsibilities.
For example:
UserAjaxIncidentAjaxCatalogAjax
depending on the application's needs.
Separate Server-Only Logic From Client Exposure
Suppose you have a large server-side service:
1AssignmentServiceThe browser only needs one small piece of information from it.
You do not necessarily need to make the entire service client callable.
Instead, a small client-callable Script Include can call the server-side service internally.
That creates a clean boundary.
The architecture becomes:
Client Script
→ Ajax Script Include
→ Server Service Script Include
→ Database / business logic
This can keep client exposure much narrower.
Example: Reusing a Server Service From GlideAjax
A client-callable Script Include might contain:
1getSuggestedGroup:2 function() {3 var category =4 this.getParameter(5 'sysparm_category',6 )7
8 var service =9 new AssignmentService()10
11 return service12 .getSuggestedGroup(13 category,14 )15 }Now the actual assignment algorithm remains in a reusable server-side service.
GlideAjax merely exposes the specific operation the browser needs.
Script Includes and Scripted REST APIs
A Scripted REST API may also need application logic.
Avoid implementing an important rule only inside the REST resource script.
Instead, the REST resource can:
- validate the request
- call a Script Include
- format the response
For example:
1var service =2 new AssignmentService()3
4var result =5 service.assignIncident(6 incidentGR,7 groupSysId,8 )The same service can potentially be reused by other server-side entry points.
Script Includes and Scheduled Jobs
Scheduled scripts often need logic that is also used elsewhere.
For example:
Find stale Incidents and run escalation processing.
The Scheduled Script Execution can identify the records and call:
1var service =2 new IncidentEscalationService()3
4service.process(5 incidentGR,6)The schedule decides when processing happens.
The service decides how escalation works.
Script Includes and UI Actions
A UI Action may initiate a business operation.
Instead of embedding a large amount of server-side logic directly in the UI Action, it can call a Script Include.
For example:
1var service =2 new ApprovalService()3
4service.requestApproval(5 current,6)The UI Action remains an entry point.
The reusable service owns the process.
Script Includes and Business Rules
This is one of the most common combinations.
A Business Rule answers:
When should something run?
A Script Include answers:
How does the reusable business logic work?
For example:
1(function executeRule(2 current,3 previous,4) {5 var service =6 new IncidentEscalationService()7
8 service.process(9 current,10 )11})(current, previous)This makes the record-triggering behavior easy to understand.
Don't Call a Business Rule From Other Scripts
A Business Rule is designed around record lifecycle execution.
If a UI Action and Business Rule need the same business logic, do not attempt to make one entry point call the other.
Move the shared logic into a Script Include.
Then both can call it.
That creates a cleaner reusable boundary.
Keep Security Separate From Code Reuse
A Script Include helps centralize logic.
It does not automatically make the operation secure.
You still need to consider:
- ACLs
- roles
- caller access
- application scope
- client-callable access
- validation
Security requirements should be designed explicitly.
Avoid Hard-Coded sys_id Values
Reusable Script Includes should be especially careful about hard-coded identifiers.
For example:
1if (2 groupSysId ===3 '46f3...'4) {5 // ...6}may create problems when moving between:
- development
- test
- production
where referenced records may differ.
When possible, use:
- system properties
- configuration tables
- data-driven references
- application configuration
Reusable code becomes more portable when environment-specific data stays outside it.
Avoid Hard-Coded Business Rules Too
A reusable method should not require code changes every time business configuration changes.
For example, instead of:
1if (2 category === 'hardware'3) {4 group = '...'5}consider whether category-to-group mapping belongs in:
- a configuration table
- decision data
- system properties
- another appropriate platform configuration mechanism
Code should represent logic.
Configuration should represent values that administrators are expected to change.
Use Script Includes to Create Clear Boundaries
Good Script Includes often represent architectural boundaries.
For example:
IncidentEscalationService
Owns Incident escalation rules.
AssignmentService
Owns assignment decisions.
ApprovalService
Owns approval operations.
These are easier to reason about than a collection of unrelated scripts that each contain fragments of the same feature.
A Practical Script Include Checklist
Before creating a Script Include, ask:
Will this logic be reused?
A Script Include is a strong candidate.
Is the current script becoming difficult to read?
Extracting a meaningful responsibility may help.
Does the logic represent a reusable application operation?
Consider a focused service-style Script Include.
Is this merely three simple lines used once?
A new abstraction may not be necessary.
Does the method have clear inputs?
Pass them explicitly.
Does it have a predictable return type?
Make the method contract easy to understand.
Does it write to the database?
Make that behavior obvious.
Does the browser need to call it?
Only then consider client-callable configuration.
Is it exposed to the client?
Validate inputs and access carefully.
Does it query the database heavily?
Consider performance and query frequency.
Common Script Include Mistakes
Creating one massive utility class
Keep responsibilities focused.
Moving code without improving the architecture
A large Business Rule inside a large Script Include is still difficult code.
Depending implicitly on current
Pass the required record or values explicitly.
Returning unpredictable data types
Create clear method contracts.
Hiding database writes inside read-sounding methods
Make side effects obvious.
Copying the same GlideRecord query into many Script Includes
Centralize meaningful shared logic.
Making everything client callable
Expose only what the browser actually needs.
Trusting GlideAjax parameters
Validate client input.
Running database queries inside frequently called loops
Reusable does not mean free.
Hard-coding environment-specific identifiers
Keep configuration outside reusable code when possible.
A Better Layered Architecture
Imagine an Incident application with assignment logic.
A clean architecture could be:
Client Script
Detects a relevant form change.
GlideAjax
Requests a suggested assignment.
IncidentAjax
Provides the controlled client-callable endpoint.
AssignmentService
Contains the reusable server-side assignment logic.
GlideRecord
Retrieves required records.
Business Rule
Enforces the authoritative assignment rule when the record is saved.
Now each layer has a clear responsibility.
Entry Points vs Reusable Logic
One useful architectural distinction is between entry points and services.
Entry points include:
- Business Rules
- UI Actions
- Scheduled Jobs
- Scripted REST APIs
- Client Scripts through GlideAjax
They answer:
What caused this operation to begin?
A Script Include can contain the reusable service that answers:
How should the operation work?
This separation makes ServiceNow applications much easier to evolve.
A Practical Example
Suppose the organization has one rule:
Determine the support group for an Incident based on category.
This rule is needed by:
- a Business Rule during save
- a Client Script for a suggested value
- an integration
- a Scheduled Script correcting legacy records
Instead of four implementations, create:
1var AssignmentService =2 Class.create()3
4AssignmentService.prototype = {5 initialize:6 function() {},7
8 getGroupForCategory:9 function(category) {10 if (!category) {11 return ''12 }13
14 var mappingGR =15 new GlideRecord(16 'u_assignment_mapping',17 )18
19 mappingGR.addQuery(20 'u_category',21 category,22 )23
24 mappingGR.addQuery(25 'u_active',26 true,27 )28
29 mappingGR.setLimit(1)30
31 mappingGR.query()32
33 if (34 mappingGR.next()35 ) {36 return mappingGR.getValue(37 'u_assignment_group',38 )39 }40
41 return ''42 },43
44 type:45 'AssignmentService',46}Now every caller shares one mapping implementation.
Business Rule Caller
The Business Rule might use:
1(function executeRule(2 current,3 previous,4) {5 var service =6 new AssignmentService()7
8 var groupSysId =9 service10 .getGroupForCategory(11 current.getValue(12 'category',13 ),14 )15
16 if (groupSysId) {17 current.setValue(18 'assignment_group',19 groupSysId,20 )21 }22})(current, previous)Because this could be a Before Business Rule, it can modify current without needing an additional current.update().
GlideAjax Caller
A client-callable wrapper could ask the same service:
1getSuggestedGroup:2 function() {3 var category =4 this.getParameter(5 'sysparm_category',6 )7
8 var service =9 new AssignmentService()10
11 return service12 .getGroupForCategory(13 category,14 )15 }Now the browser and server-side enforcement use the same assignment rule.
That greatly reduces the risk of inconsistent behavior.
The Client Is Still Not the Authority
Even though the Client Script can request the suggested group, the server-side rule remains authoritative.
The browser may display:
Suggested Assignment Group: Service Desk
But when the record is saved, the Business Rule can still apply the real rule.
This follows the same principle from our Client Scripts vs Business Rules guide:
Client logic improves the interaction.
Server logic protects the business rule.
When Not to Use a Script Include
A Script Include is not necessary for every piece of server-side JavaScript.
You may not need one when:
- the logic is extremely small
- it is used only once
- the behavior is tightly coupled to one specific record lifecycle event
- extracting it would make the code harder to follow
- a declarative platform feature already solves the requirement
Abstraction should reduce complexity.
It should not exist merely because reusable code is considered fashionable.
Prefer Platform Features When Appropriate
Script Includes are powerful, but they are still custom code.
Before writing one, consider whether the requirement is better handled through:
- Flow Designer
- Data Policies
- UI Policies
- ACLs
- Decision Tables
- platform configuration
- another purpose-built ServiceNow capability
Good ServiceNow architecture uses scripting where scripting adds value.
A Useful Mental Model
Think of a ServiceNow application like this:
Entry Point
Business Rule
UI Action
Scheduled Job
REST API
GlideAjax
↓
Script Include
Reusable application logic
↓
Platform APIs
GlideRecord
GlideAggregate
GlideSystem
other APIs
↓
Data / Platform
ServiceNow tables and services
The entry point can change without forcing the core business logic to be rewritten.
That is the main architectural value of Script Includes.
Why Script Includes Scale Well
As an application grows, the same business rules are often needed from more places.
Today:
- one Business Rule
Tomorrow:
- UI Action
- Scheduled Job
- Integration
- GlideAjax call
- Scripted REST API
If the real business logic already lives in a focused Script Include, adding another caller becomes much easier.
Instead of copying the implementation, the new entry point reuses the existing service.
Conclusion
Script Includes are one of the most important tools for building maintainable server-side ServiceNow applications.
They give reusable JavaScript logic a clear home.
Instead of duplicating the same implementation across:
- Business Rules
- UI Actions
- Scheduled Jobs
- REST APIs
- other server scripts
those entry points can call one maintained implementation.
A strong Script Include should have:
- a clear responsibility
- meaningful method names
- explicit inputs
- predictable outputs
- intentional database side effects
- efficient queries
- appropriate application access
- careful client exposure
Client-callable Script Includes extend the same architecture to the browser through GlideAjax.
But client exposure should be deliberate.
Validate inputs, return only the required information, and keep authoritative business rules on the server.
A useful rule of thumb is:
Entry points decide when something happens.
Script Includes define reusable ways of doing it.
Once you begin thinking of Script Includes as internal application APIs rather than just miscellaneous helper files, ServiceNow server-side architecture becomes much easier to organize, reuse, test, and maintain.

Learn how to use ServiceNow GlideRecord for server-side queries, filters, updates, inserts, reference fields, encoded queries, and performance-friendly scripting.

Learn the difference between ServiceNow Client Scripts and Business Rules, when to use each, common mistakes, performance considerations, and practical examples.