Code Philosophy - Through Code Samples - ServiceNow Community
Javier Arroyo
Kilo Guru

This article is about looking into what I consider the most important aspect of successful programming: philosophy. It is an ideal that drives the quality of code. The more grounded an ideological outlook on code, the better the chances that it will be successful. However, this philosophy has to have some measure, some quantifiable from which to derive success. First and most important I believe in empowering developers, rejecting the adages of 'When in Rome, do as the Romans'', "Code at a junior level so that they understand". 

When in Rome, do as the Romans is rejected because following along doesn't foster growth.

Code at a junior level so that they understand is ignored because readability and low skilled aren't mutually inclusive. 

The overall philosophy on code:

  1. Learn as much as you can about your trade. In terms of coding and ServiceNow, this means JavaScript.
  2. Making something simple isn't easy. Were it the case, everyone would do it

Code specific philosophy

  1. Single line entry point
  2. Decrease reader's interpretation time ( terse, exact and 'unambiguous')
  3. Code for human consumption (write code as if writing a book)
  4. Group code into smaller logical units (Layers of abstraction)
  5. Build for now yet be scalable. Scalability allows code to grown without having to continue adjusting the same code as the code base grows.
  6. Code what is to happen, not how it's happening

 

The Client Script below will be used to demonstrate the philosophy 

function onChange(control, oldValue, newValue, isLoading, isTemplate) {


  if (newValue == 'something') {

    g_form.setMandatory('fieldName1', true);
    g_form.setMandatory('fieldName2', true);
    g_form.setMandatory('fieldName3', true);
    g_form.setMandatory('fieldName4', true);
    g_form.setMandatory('fieldName5', true);
    g_form.setMandatory('fieldName6', true);
 
  } else if (newValue == 'somethingElse') {

    g_form.setMandatory('fieldName7', true);
    g_form.setMandatory('fieldName8', true);
    g_form.setMandatory('fieldName9', true);
    g_form.setMandatory('fieldName10', true);
    g_form.setMandatory('fieldName11', true);
    g_form.setMandatory('fieldName12', true);

  } else {

    g_form.setMandatory('fieldName13', true);
    g_form.setMandatory('fieldName14', true);
    g_form.setMandatory('fieldName15', true);
    g_form.setMandatory('fieldName16', true);
    g_form.setMandatory('fieldName17', true);
    g_form.setMandatory('fieldName18', true);

  }

}

 

So that it becomes this Client Script

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
  
  ChangeStateManager.setMandatoryFields({value: newValue, mandatory: true});

}

Upon seeing this Client Script we know what is happening. There is no doubt that fields are being made mandatory. Unlike the original script where each line has to be interpreted individually to know if it will be mandatory or not. Right at a glance the only thing that can happen is that fields become mandatory. No need to identify each condition to understand that the script is setting fields to mandatory. What fields is not necessary to know here. All that should be is what is happening. Let the how be somewhere else, and in an easier place to locate.

This single-line-of-code is arrived by breaking down the code into pieces.

A UI Script that is loaded to the front-end through SNow's ScriptLoader.

var ChangeStateManager = (function initializeLibrary(g_form, g_scratchpad){


  var doSetMandatoryFields = function doSetMandatoryFields(params) {
    
      var fields = g_scratchpad.CHANGE_REQUEST_MANDATORY_FIELDS_STATE[params.value];
     
      fields.forEach( function setMandatory(field) {
        g_form.setMandatory(field, params.mandatory);   

      })();

  };

  return {
     setMandatoryFields: doSetMandatoryFields
  }
 
})(g_form, g_scratchpad);

 

The above script is called ChangeStateManager. It has just one function called setMandatoryFields. It uses a forEach to loop through fields retrieved from an object hosted by g_scratchpad.

This script itself encapsulates the further details. While it should indeed be broken down a little further to prevent explicit dependencies, it still serves a very exact purpose. So, we can live with that for a very long time. the item CHANGE_REQUEST_MANDATORY_FIELDS_STATE is an Object that contains an array of values. The values, given what the code is doing can only be a list of field names.

This one script is now able to handle all mandatory state changes, regardless of the state. No need to pollute the code with IF/Else or switch statements.  Grab the fields by accessing a key in the Data Dictionary.

NOTE: it should become clearer at this point that one more abstraction would turn setMandatoryFields into a utility function to be used from any object, with any field using any g_form.methodName. The need for multiple Client Scripts and UI Policies can be bypassed.

Still, however, missing from the equation are the field names to set to mandatory. Where are those? Those guys are found inside a table, awaiting an admin to adjust them as necessary.

For demonstration purposes the fields will be limited to two items. The drop down values can come from sys_dictionary or sys_choice table. The location doesn't matter as long as the right values are available.

1. A State Field to match item state of the change.
2. A list collector to host the fields names that will become mandatory.

Once the table is available and populated with values, and admin may adjust them as necessary bypassing the need for dev cycles for trivial update as a mandatory field.

 

To fill the g_scratchpad with the object from the table a business rule is created as such.

(function executeRule(current, previous){
  //on display Business Rule. 
  ChangeScratchpadManager.bootstrap(g_scratchpad);

}(current, previous);

Note that it is another layer of abstraction with a single line entry point. It contains a Script Include ChangeScratchpadManager that contains logic necessary to build the scratchpad.

The script include then does this

var ChangeScratchpadManager = {
   bootstrap: bootstrap(params) {
      var mandatoryFields = retrieve({ table: ChangeScratchpadManagerOption.table, 
                             encodedQuery: ChangeScratchpadManagerOption.encodedQuery + 
                              params.state}
                          )
      makeScratchpad( {fields: mandatoryFields, scratchpad: params.scratchpad} );                       
  }
}

The script include contains yet more abstractions. All of them break code into smaller, more logical units. Each becoming more and more manageable. Now, as we progress with skill, even these layers become decoupled from the explicit dependanies, and instead functionality is injected way of parameters or through factories. But for now, this serves for a good demonstration to the philosophy listed above.

There is no doubt that as each code is broken down into pieces it the setup grows more skilled, and certainly more complex than a simple single static in one place that only requires a change right there. Structure to the code has been to introduce scalability, and ease of maintenance. A developer is no longer required to set mandatory fields (imagine were this done for all the g_form field behavior methods), whenever a new mandatory field is added, or one removed, an admin updates the record on the table and the change instantly appears on the form. N update to the State Option table on the fly by an admin does. States can even be added to account for changing states. or more dev cycles or cost associated with the changes. 

The layers of abstraction in ChangeScratchpadManger are

  1. retrieve (a GlideRecord wrapper that takes a table and an encoded query to retrieve records)
  2. makeScratchpad (a factory function that takes a GlideRecord resultset and builds a scratchpad from it)
  3. ChangeScratchpadOption (a DataDictionary to host the values required to execute a GlideRecord)

 

Though the code is small and from the identifier names logical enough to follow, there is a lot of knowledge required to understand how it works, and structural benefits. Such as understanding that changing an Object's value makes the change visible outside the object. there is no need to return the object to have it's value persist. 

The structural benefit is to leverage one line of code to handle any mandatory field requirement. No more readjusting of code, and code duplication. Though I've used the names specific to Change in our example, feeding it different parameters will cause it to work on any form, based on any field value. All of this is controlled by configuration, rather than code.

Smaller pieces of code are certainly easier to digest than longer, drawn out pieces of code. (anecdote: I just interviewed a programmer and gave him and easy, as he called it, code to read, it took him about 20 minutes to gain a fairly inaccurate understanding of what was happening. That can't happen, that's 20 minutes are better used actually coding, rather than understanding)

 

Three conditions and 18 setManatory were replaced by ChangeStateManager.setMandatoryFields(newValue). The rest is an architecture to support all mandatory fields based on some field value. From the back end, to the front end. 

Lastly, what does the scratchpad object used to set fields mandatory looks like:

g_scratchpad.config = { 
CHANGE_REQUEST_MANDATORY_FIELDS_STATE: {
     new: ['requested_by','short_description','description'],
     assess:  ['cab_required','cab_delegate']
  }
};

 Apologies for not detailing the layers of abstraction in ChangeScratchpadManger. I'll briefly speak of them as not to leave them up in the air.

retrieve is a universal wrapper to GlideRecord that pulls any records from any table. 

var records = new GlideRecord(tableName);
records.addEncodedQuery(encodedQuery);
records.query();

makeScratchpad

loops through a list of GlideRecords and converts them to objects and adds them to the scratch pad 

while (records.next() ) {
  //build the scratchpad. This I have actually turned into a utilty function that turns 
   given a gliderecord, and an Object Literal with field names as keys, populates the Object Literal with glide record values.

   //for now, think of some sort of key = field value
}

ChangeScratchpadOption

 

{
  Key1: {
    table: 'tableName',
    encodedQuery: 'some sort of encoded query'
  }

}

Always remember, first get a grip of your code philosophy then become pragmatic; pragmatic can't help when junk is placed in the garbage can, then we take it out and blame the garbage can for returning garbage. (i forget who said that)

Version history
Last update:
‎08-01-2018 07:40 AM
Updated by: