Sunday, February 3, 2019

This is the first in hopefully a series of articles on incorporating useful functional programming techniques while writing JavaScript in ServiceNow.

How many times have you seen code like the following?

var grIncident = new GlideRecord('incident');
grIncident.addQuery('active=true^state=1');
grIncident.query();
while (grIncident.next()) {
    // do some processing with the GlideRecord
    gs.info('processing ' + grIncident.getDisplayValue());
}


This boilerplate code is so common in the platform, you probably use a code snippet or macro to type everything for you, then go back and edit as needed.

Any time you see the same code over and over, an alarm should go off in your head. You may have heard the about the DRY (Don't Repeat Yourself) principle. Why is it then, that we as ServiceNow developers violate this design principle virtually every day when we repeat this boilerplate code over and over again?

If you use such a code snippet, what are the things you end up going back to edit? Most likely they include the table name, query string and the processing to be done.

This gives us a hint to what things are fixed and what things change. In software design, the best practice is to separate the things that change from the things that remain the same. An example of this is using system properties to enable changes to code behavior without actually changing the code.

Can we write the GlideRecord query with looping logic in such a way that we separate the things we change each time from the boilerplate stuff that remains same?

Yes, we can, and it is the first step we'll take toward using a functional approach for our query logic. Here's a helper function named query that takes three parameters - the very three things we change after using a typical GlideRecord query code snippet:

function query(table, query, fn) {
    var gr = new GlideRecord(table);
    gr.addQuery(query);
    gr.query();
    while (gr.next()) {
        fn(gr);
    }
}

Here's an example of using our query helper to process new incidents:

function processNewIncident(grIncident) {
    gs.info('processing ' + grIncident.getDisplayValue());
    // do some processing with the GlideRecord
}

query('incident', 'active=true^state=1', processNewIncident);
Here's an example of re-using our helper function for something completely different:
function processClosedHighPriorityIncident(grIncident) {
    gs.info('processing ' + grIncident.getDisplayValue());
}

query('incident', 
    'active=false' + 
    '^ priority=1 ^OR priority=2', 
    processClosedHighPriorityIncident);

No more repeating boilerplate code, and by encapsulating our processing of an individual GlideRecord into a separate function, separating the processing concern from the data retrieval concern (another best practice - Separation of Concerns or SoC) our code becomes more readable and maintainable.