Javier Arroyo
Kilo Guru

This article is about my observations regarding ServiceNow coding practices in nearly a decade. If you are not really interested in code. This might not be for you.

On my Background - to help you asses the source of my views . Feel free to skip to Onto my Observations. (TL;DR - I've been a programmer all my life)

I've been a programmer at various degrees for some 25 years, becoming relatively proficient in some dozen languages or so.  I've coded in procedural, OO and the past 3 years in functional paradigm. Before becoming interested in code complexity and clean code I was really a mediocre programmer unaware that there was more to programming than API docs. Today, I consider myself as a student who continuously makes mistakes and uses them to grow.  

As a result of not getting comparable quality from consultants in ServiceNow as I had been accustomed in donet, Java and Salesforce, I delved head first into figuring out how use my experience in ServiceNow. My interest went shifted from getting something done to learning more about programming.

During my ServiceNow career (aspen) I've focused mostly on ITSM, touching the Portal & ITOM as needed. The size of the instances have ranged from 2K to 100K users. My employers have collaborated with over a dozen ServiceNow partners, where I've had the pleasure to observe, learn, oversee, code review, teach and rewrite code. I don't worry about perfect code, as much as I worry about making it clean and readable. 

 

State of code
As a collective, the ServiceNow community is still in the infant stages of programming. This is neither good, or bad. It's just the way things are. Because of the initiative to build a no-code/low-code platform, each passing release decreases the dependency on coders. 


Code Expectations
Everyone must care about quality. Good code is achieved through gradual improvement, so each day programmers must be better than the previous day. The way to improve is to read, practice, read, practice and practice. Read not just about JS but, about programming theory, patterns, ideas, other's code... 

For those who care to delve deeper into the craft, "The Pragmatic Programmer: From Journeyman to Master" second edition by Andrew Hunt and David Thomas has a wealth of information. The first version is freely available if buying one is not an option. It is not about JavaScript, it's about being a programmer.

 

Onto My Code Observations

1. The ServiceNow space lacks Engineers.

For better or worse, the current state is that our strength is on the process side. Code engineers are not having an impact. Perhaps because their content is difficult to find, or they aren't engaged in the community. For whatever reason quality code content is lacking. By quality code content I don't mean the usual I-discovered-something while being unaware of it's pitfalls. But, a solution that breathes coding knowledge. One that identifies the person as having been exposed to ideas most of us haven't. Using JS as its programming language, this content should be more visible.


2. JavaScript is not a strength. Intentionally or not, JS as a whole in SNow is still at a novice level. 

In about a decade of programming in ServiceNow, the most pervasive response to coding at a beginner's level is that code is built for lower skilled programmers to maintain. The response is an indictment more than a valid reason. I believe this outlook prevents the community from growing. 


3. Coding advice is either out-dated, or the very same practices that lead to the initial problem being solved. 

Out-dated because advice commonly assimilates to practices irrelevant for some 50 years since OO replaced procedural programming. Also, because JavaScript advice is rooted in version JS3 released in 1999. It lacks information from JS5 introduced in 2009, and in ServiceNow since Helsinki. While JS5 is still outdated it is the version used in the backend, advice should at least be at that level. From sn docs: Your script will follow standard ECMAScript5 behavior. Lets get our advice there.


4. Focus on completing a task bypasses solving a problem (aided by outdated advice).


Example:
Question:
    I need to loop to get field values.
Advice:
  use a for-loop. 

While the advice correctly achieves the task it doesn't really solve the larger problem.
Coding is filled with repeatable tasks. Patterns that can be turned into solutions to address them, dramatically decreasing the amount of work required the next time the same problem is encountered. By not identifying the pattern, we can not move forward.


For example, extracting values from a GlideRecord. How many times have we not done this,  or help someone do it?
Inside extracting values are multiple, independent processes taking place. By separating them atomically, each piece can become part of uncountable compositions. The impact is less interpretation caused by eyeing through extra syntactical sugar. Code maintainers will have a far easier time supporting processes. And, process programmers won't have to reinvent the wheel.

The fragment of code below on the right side column is a partially solved problem. Despite it's incomplete state, would you really prefer typing the left side snippet each time an array of values is needed from a GlideRecord, or just the field names? The right is a more scalable solution, and inherently less prone to bugs because there is less to type. 

Completing the taskPermanent Solution
var values = [];

values.push(current.number+'');
values.push(current.description + '');
values.push(current.short_description + '');
values.push(current.assignment_group + '');
var fields = ["number","description","short_description","assignment_group"];
var values = collectValuesAsArray(gr, fields);

 

 

5. Advanced coding is mistaken with cryptic code.
Cryptic code is so not because its clever, nor advanced. It is so because it increases the amount of time required to understand it. Advanced code should exhibit dumb code characteristics. That is why advanced code, i.e dumb code, is universally said to minimize ambiguity: it reduces interpretation times.

Our inability to build dumb code is what leads to the point of view that advanced constructs are advanced code, therefore we must shy away from it for JRs to maintain. Also keep in mind that dumb code is not familiar code. Do not conflate that either. 

For example. This is simple, familiar code

        var glide = new GlideRecord('incident');
        glide.addQuery('short_description', 'CONTAINS', incNum);
        glide.addQuery('description', 'CONTAINS', 'some text');
        glide.query();

And this is dumb code

var incidents = getIncidentThatContainShortAndDescWith('INC001234', 'SNOW'); 

One asks to be read, the other to be interpreted.


6. Naming is still a challenge
Names don't often convey intent, or are ambiguous. 
Util has become a favorite ScriptInclude suffix.
Generality is introduced at the wrong times.


7. Struggles with Idiomatic Programming
Idiomatic programming is writing code as language specifications advice a construct be made.


Example

for (var x=0; x < something.length; x++) {
    //do something
}

As basic as a loop is to programming, or as familiar as they are to many of us. That piece of code is unreadable. Call someone over, preferably a non programmer and read it out loud. How easy was that to read? Think of it, how would code inside that block further affect readability? Any extra time spent interpreting idiomatic constructs hinders maintaining code said to be written for beginner level programmers.


8. Erratic Code Articulation.
Code can be structured so that minimal interpretation is required. Because we often code procedurally, articulation can at best be a bundle of syntax. Articulation requires more than just pragmatically typing characters.

Rethinking the for loop

forEach(doSomething)

By going from how to loop, to what to loop makes it fairly obvious at-a-glance. Reduce time required to see what the code is doing.


9. Struggles composing code flow

Coding is bundled together as if just a parking lot for characters. It is built as if the human reading the code was the computer interpreting it. 

What does that mean?
Code must be broken down as if reading a book. A book has a a table of contents, chapters, paragraphs logically grouped, punctuation, white spaces, etc. As pages are turned, topics are further explored until deep within that book, details are revealed. Details aren't just presented ad-hoc when opening the first page. Books follow predefined format because language has taught us that communication is most efficient chunks at a time. It starts with easily consumable information until increasingly complex. The ground work to introduce the most intimate parts of a program need to be composed at the top so that once that advanced logic is opened, it doesn't WTF.

Common practices yield

(function executeRule(current, previous /*null when async*/) {
	
	// Return if action is aborted
	if(current.isActionAborted())
		return;

	var taskId = current.getValue('task');
	
	var metricId = current.getValue('metric');

	// For audit close all associated classes if glide.cmdb.health.autoCloseAuditTasks is set to true
	if (metricId == '24f72b5137130200f212cc028e41f155') {

		if (gs.getProperty("glide.cmdb.health.autoCloseAuditTasks", "false") == "false")
			return;
		
		var taskList = [];
		
		if (gs.nil(taskId))
			return;
		
		var additionalTask = current.getValue('additional_tasks');
		if (!gs.nil(additionalTask)) {
			taskList = additionalTask.split(',');
		}
		
		taskList.push(taskId);
		var gr = new GlideRecord('task');
		for (var i in taskList) {
			var t = taskList[i];

			if (gr.get(t)) {
				gr.setValue("state", '3');
				gr.update();
			}
		}
		return;
	}		
	
	if (GlideStringUtil.notNil(taskId)) {
		// Check if this is a de-dupe task
		var grTask = new GlideRecord("task");
		grTask.get(taskId);
		// skip if the task is already in one of the inactive states
		if (grTask.active == false)
			return;
		if (grTask.getRecordClassName() == "reconcile_duplicate_task") {
			// check if there are more than one other duplicate CI associated with the task
			var grDuplicateResult = new GlideRecord("duplicate_audit_result");
			grDuplicateResult.addQuery("follow_on_task", taskId);
			grDuplicateResult.addQuery("duplicate_ci","!=", current.getValue("ci"));
			grDuplicateResult.query();
			if(grDuplicateResult.getRowCount()<1)
				grTask.setValue("state", '3');
		} else {
			grTask.setValue("state", '3');
		}
		grTask.update();
	}
	
})(current, previous);

That code above is asking the maintenance coder we worried about by keeping the script simple and familiar, to dissect that frog. There is no table of contents to go by, no synopsis, no executive summary, paragraphs handle random thoughts. It so darn busy. Not even comments (even if misused) were added. The reader is asked to fully understand each and every piece (can't forget an obscure sentence in the very first page of the book) to get a full understanding of 40 lines of code. Then, redo it time and the time again until it becomes familiar.



find_real_file.png

That bike pile is the state of code I have experienced in ServiceNow. Want to guess how I've been advised to address that? Comments! So I did that below. No more issues finding a bike in the image down there. Comments have decluttered it.



find_real_file.png

 

Comments added to smelly code increase interpretation times. Fix the code, reduce maintenance times, minimize clutter. This other image below, that's what we got to strive for.


find_real_file.png

While I have taken liberties with the images above. It was done to express a point. Smelly code isn't fixed by adding more noise. It requires organization.

Do not allow groups to organically figure that organization, help them. Mistakes will be made by not recognizing the effect of practices we don't fully grasp.

7. Peer reviews by peers who themselves don't know code.
This can lead to an echo chamber when unchecked. Lead to that pile of bikes with comments aimed at decluttering it. If participants have the same level of knowledge, help them. Reviews need more than guidelines, they need detailed knowledge beyond what benefits a group of people agreeing on whether to use a ternary operation or not. This is indeed a good idea, just not left on in a silo.

8. Resistance to move outside the common
Mention that misused comments are a smell, that a ternary operator is simple, that gr is not a bad variable name, that declarative programming can lead to elastic and easily understood processes, that abstraction lead to easier maintenance, that functional programming is easily achievable in JavaScript, that rhino supports curried and higher order functions... the objections mound as a presidential election. The community lacks the engineers to shed some light that the instance is not really that black box where JavaScript comes to die. Our grievances with industry practiced coding standards is directly related to our skill level.

9. OOTB code is used as example.
The only time OOTB code should be looked at is to understand what is available. Other than that, no one should be looking to learn by using OOTB as examples.

10. We are all too busy to take on the responsibility of helping others.
While is impractical to impose on others to help the community, we can all do a lil bit here and there.

11. Pro level code firms do as the Romans do when in Rome.  
When a firm comes into a new implementation, remaining behind isn't pro level code. It is some form compiled from the observations above. We've all been in calls holding a SOWs asking if code was put through a review. Yes, we follow ServiceNow best practices. Here are the documents. Plus, we got linters, reviewers, peer-reviews. Mhm, sure looks like it.

12. There is no difference between low level, mid level, high level code.
All code is bundled into some form of. SomeNameUtil

Code belongs in layers of abstraction that are then composed into a processes. The bottom layers are utility functions, and where the most language specific constructs will be found. It is where skill showcases advance constructs and topics in common idiomatic form. Hi-level code, in turn, is all single line entry point functions that calls a mid-level layer where functions define processes from the bottom layers.

//Top Layer I.E Business Rule
NewHire.broadcastNetNew(current);



//Middle Layer SI

//code
function broadcastNewNew (sys_user) {
    var netNewProcess = [getRecordValues, queueEvent, notifyHR];
    return pipe(newNewProcess)(sys_user);

}


//Bottom utility layer SI/SIs


function getRecordValues (...) {
    //some code
}

function queueEvent(...) {
    //some code
}

function notifyHR (...) {
    //
}

The snippets above are a possible representation of how to structure code. Top layer being a business rule calling a middle man, process-identifying-script-include, it then calls a bottom SI layer(s) with implementation specific code. 


13. Procedural is the de facto programming paradigm. 

Programming procedurally leads to functions that handle so much responsibility, increasing complexity. Making it nearly impossible to build single purpose functions. 

14. Not enough direction from the platform
While it is not ServiceNow's job to teach programming. It's docs should be impeccable. They aren't.

15. Instead of injecting dependences, we reach out to them from functions
Both inheritance and composition lead to all sorts of side effects. If, instead, behavior is injected into functions. Code becomes easier to handle, to adjust, to compose, to maintain. This one... requires experience to really understand why it's so helpful.

 

Happy SNowing...

Version history
Last update:
‎11-05-2020 09:25 AM
Updated by: