_
_
Back to Blog
ServiceNow
ServiceNow Best Practices

Minimizing Upgrade Conflicts with ServiceNow

Leverage inheritance to safely extend script include behavior without upgrade risk
6
min read
|
by
Rob Witty
November 6, 2024

Customizations are unavoidable, and we often need to change “Out Of Box” behaviors to fit the needs of the business. This makes us cringe a little, knowing that our modifications will create an upgrade conflict–forever into the future. 

So, let’s ponder how we can customize OOB scripts while avoiding these unpleasant upgrade conflicts.  Let's consider the worst case scenario–that major OOB script include which needs to change. How do we customize its behavior without modifying it?  The solution is actually quite simple, using the object-oriented concept called inheritance

ServiceNow Example of Inheritance

JavaScript implements inheritance by extending the object, as in this example.  Notice ServiceNow is extending the ActivityUtilsSNC object, which is read-only.  

Thus, ActivityUtils inherits all the functions of its parent: ActivityUtilsSNC.  Even though you don’t see them, you can still invoke them from ActivityUtils.  Here’s an example of another script invoking ActivityUtils.

Later in that script, they invoke its function.  

ServiceNow has provided this extended object for us so we can 1) add new functions to it and/or 2) modify its functions without incurring a hit on our next upgrade.  ServiceNow upgrades the SNC parent object and extends it to an object we can customize.  Nice. 

But what if ServiceNow doesn’t have an extended object? No problem. We’ll make our own. That way, we can customize the behavior of the OOB object via our custom extended object and avoid an upgrade conflict.

Extending an Object  

Let’s build a pair of objects to test how this works on the ServiceNow platform.  We’ll use a background script as our sandbox. We can thus test parent-child behaviors before we start coding our solution.  Here’s our parent object; 

Now let’s extend it and override the parent object’s functionOne().  We’ll then instantiate the child object and call our new version of functionOne().

Line 20: We’re invoking the parent object’s functionOne() right inside our override of that function. You don’t have to do this.  But it’s sometimes handy, when you want to do all the stuff the parent does and then do more after it.   

Line 22: We can invoke other parent object functions as if we were the parent object. Nice. This demonstrates that the child object has full access to everything inside the parent.

Running this in a background script gives us this output: 

*** Script: initialize myParentObj. var1: Hello

*** Script: myParentObj.functionOne(): Hello World!

*** Script: myChildObj.functionOne(): WhatsUP World?

*** Script: myParentObj.functionTwo(): HELLO!

If we need to adjust the parent object’s variables in the initialize() function, we can simply override it. Best practice is to invoke invoke the parent’s initialize() function, then add your stuff after, like this: 

Notice that line 20 overrides the parent object’s variable by the same name!  So, the child object can override parent attributes. 

Running this version yields the result: 

*** Script: initialize myParentObj. var1: Hello

*** Script: initialize myChildObj. var1: WhatsUP

*** Script: myParentObj.functionOne(): WhatsUP World!

*** Script: myChildObj.functionOne(): WhatsUP World?

*** Script: myParentObj.functionTwo(): HELLO!

Using this object-extension strategy can be helpful, but there’s a rub. 

But the Business Rules!

But what if the OOB business rule is invoking the OOB script include and not an extension of it? This is the rub, yes? The upside is that it’s generally easier to remediate a business rule than a script include during the upgrade process–if we ignore those ugly, 200-line business rules, which we won’t talk about.

Several strategies for avoiding–or at least minimizing–an upgrade conflict regarding business rules exist.

  1. Modify the OOB rule to invoke your custom-extended script include. This incurs an upgrade conflict, but the code change is minimal. You only need to change the name of the script include to the name of your custom-extended script include. 
  2. Inactivate the OOB rule and create a new rule to replace it. This incurs an upgrade conflict as well.
  3. Let the OOB rule run “as is” and create a custom business rule to run after it, with the intended purpose to override or modify the OOB rule that ran just before it. So, for example, if the OOB rule is sequenced at 100, you could sequence your custom rule at 101. This avoids an upgrade conflict, and you get the intended modification. The caveat here is that the engineer who comes after you might get confused, so document your custom business rule to help them understand.

I recommend option #3 only if you’re dealing with a before business rule, where you can modify data before it is written to the database. Once data hits the database, there are numerous things in the platform that can trigger other processes. At that point, it’s generally too late to amend the targeted OOB behavior.  

Conclusion

As software engineers, we strive to craft solutions that don’t generate additional maintenance overhead. I’ve mentioned a few strategies for minimizing upgrade conflicts, and I’ve included the code for testing object-extension behaviors below.  Try it out and see how to employ extended objects in your solutions.  


var myParentObj = Class.create();
myParentObj.prototype = {
   initialize: function() {
       this.var1 = 'Hello';
       gs.info('initialize myParentObj. var1: ' + this.var1);
},
   functionOne: function() {
       gs.info('myParentObj.functionOne(): ' + this.var1 + ' World!');
   },
   functionTwo: function() {
       gs.info('myParentObj.functionTwo(): HELLO!');
   },
   type: 'myParentObj'
};


var myChildObj = Class.create();
myChildObj.prototype = Object.extendsObject(myParentObj, {
   initialize: function() {
       myParentObj.prototype.initialize.call(this);
       this.var1 = 'WhatsUP';
       gs.info('initialize myChildObj. var1: ' + this.var1);
   },
   functionOne: function () {
       myParentObj.prototype.functionOne.call(this);
       gs.info('myChildObj.functionOne(): WhatsUP World?');
       this.functionTwo();
   },
   type: 'myChildObj'
});


var myObj = new myChildObj();
myObj.functionOne();

Interested to learn more? Reach out to us at 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.
you might also like
back to blog