_
_
Back to Blog

Crafting Smart Objects in ServiceNow

Replace util scripts with smart objects in ServiceNow by adding context, encapsulating logic, and improving code design
4
min read
|
by
Rob Witty
April 15, 2025

How often have we seen a script include with “Util” in the name? “Util” is really an abbreviation for “bucket of stuff.”  They look like this, but usually not as organized: 

“Util” script includes, like buckets, are dumb. They’re just a container to collect functions–sort of like a junk drawer. If there’s any relationship between functions, then something outside the bucket better know that, because the bucket’s not going to help you. 

What if we created smart objects instead of buckets?

What is a smart object? 

We might think that a “smart object” knows a lot of stuff about a great many things. But that would violate the Separation of Concerns principle–a fundamental principle of good software design. So by “smart object” I mean an object that concerns itself with one thing. That “thing” is usually a real thing in your system, like a Purchase Order. The smart object will know about a purchase order’s data and the logic required to service that data. A smart object hides complexity from, and provides service functions to, the rest of the system for its “one thing.”

Here’s an example from an OOB object called ProcurementUtils. This object provides a lot of different functions, doing different things. Notice how you have to provide the PO number for each function. Why?  Because the util bucket has no persistent context. And because of this, the function names must provide some context in order to know we’re dealing with a PO and not one of the many other records the object may handle. So we get function names like this: 

Let’s imagine that we crafted a smart object instead.  Let’s call it PurchaseOrder. It’s not a bucket of functions around the PO. Rather, we treat this smart object as if it were a PO.  We instantiate our PurchaseOrder object with a PO sysid only once. Thereafter, all functions pertain to that PO. Notice how this provides context and simplifies the code. Here’s an example of the same functions using our smart PO object:

Better!  And you don’t have to pass the PO number for every function because the smart object has context. Nice. 

But there’s more benefit here than just simplified function names and less argument-passing. Consider our “utils” object, ProcurementUtils.  Anything that invokes it must have specific knowledge about purchase orders and their relationships to other tables. As a consequence, knowledge about Purchase Orders is dispersed across the platform.  

By contrast, our smart object encapsulates knowledge about PO’s into one place–itself.  If anything in the system needs to engage a PO process, they need only ask the PurchaseOrder object. When it comes to a PO, it’s the smartest object in the system. We want it to be smart, so other components in the system don’t have to be

Let’s consider an example. 

Unfortunately, most ServiceNow script includes are “util” buckets. But this won’t stop us from creating smart objects for our scoped apps and custom solutions, yes? Let’s suppose we’re building a scoped app that automates server patching. Here are some guidelines for creating a smart object. 

  1. Create an object that represents an identifiable “thing” in the system. Typically, this means a table. In our new app, we have a table called Patch Definition, where we store the data for what constitutes a patch.  So we’ll create an object called PatchDefinition
  1. Upon instantiation, a smart object (usually) represents a single row in a table.  So in our example, the instantiation would look like this: 

var patchDef = new PatchDefinition(patchId);

The variable patchDef now represents a row on our Patch Definition table. Any data you need to consume from the row, any work you need to do with the row, any updates you need to make to the row–can all be made by function calls to the patchDef object we just instantiated. Here are some examples: 


	patchDef.isScheduledForDeployment();
	patchDef.checkForLatestUpdates();
	patchDef.isApproved();
	patchDef.addToChangeRequest(aChangeRequest);
	var serverList = patchDef.getTargetServers();
  1. What if we want to create a new Patch Definition row

var patchDef = new PatchDefinition(); // pass nothing.
patchDef.setTargetServers(serverList);
patchDef.setPatchMods(modificationList);
patchDef.setTargetInstallDate(someDate);
patchDef.update();

Notice this script snippet doesn’t have to know what table is involved, or what fields to update, or whether the data passes edits, etc. All those details are hidden, known and performed by the smart object. Thus, knowledge about a patch definition is not scattered throughout our system. It’s in one place. Nice. 

Let’s think about software design. 

Crafting smart objects aligns with industry-acknowledge software design principles, like Separation of Concerns and the Single Responsibility Principle.  Within the ServiceNow environment, we don’t often pause to ponder software design. It’s so easy to just code snippets of logic here and there. And for simple applications, this works well. But when building more complex and nuanced applications, these principles serve us well and reward us with apps that are easier to maintain, grow, and scale.  

Interested to learn more? Reach out to chat@rapdev.io.

Written by
Rob Witty
Boston
Software developer crafting solutions for over 40 years on various platforms, databases, and languages. IT is about mastering one new technology after another. Hiker, traveler, admiring the wonder in things great and small.