- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 02-21-2019 07:24 PM
Research shows that 80% of the time spent working with code goes to reading it; that as familiarity with code increases, readability increases. What this means is that cryptic code becomes trivial to decipher with familiarization, it also means that in a few months from now, future me will not be as familiar with that cryptic code base and will again have to spend time learning what should have been easy to read.
A developer must make a conscious effort to make code as human read-able as skill-set allows. This often leads to abstractions that hide away implementation details, allowing processes to be written extremely simple. Simple, however, is not meant as low skilled, but a well crafted grammatical construct that dramatically reduces interpretation times. In simple terms, any reader, regardless of skill level, should be able to read a piece of code and know precisely what its doing without as little interpretation effort as possible.
as an example:
var theseIncidentValues = buildValues(IncidentDictionary.NEW_INCIDENT_DEFAULT_FIELDS, current);
insertInto(IncidentDictionary.INCIDENT_TABLE)(theseIncidentValues);
The code snippet above introduces three levels of abstraction.
1. buildValues: a utility function that given a map of fields and a current object, returns values extracted from current and returned as an Object Literal to be passed into another layer of abstraction
2. IncidentDictionary: a key/value pair that contains data signatures required for utility classes. In this case, NEW_INCIDENT_DEFAULT_FIELDS is an array (convention in JS states that items finishing with s are arrays) containing the fields that must be added when a new incident is created from (whatever current object is)
3. insertInto: a GlideRecord Wrapper that takes in the table name, then the values required to create the new record
A reader doesn't need to understand code to quickly identify that theseIncidentValues are being insertInto INCIDENT_TABLE
Compare this to:
var incident = new GlideRecord('incient');
incident.newRecord();
incident.setValue('short_description', current.getValue('short_description'));
incident.setValue('description', current.getValue('description'));
incident.setValue('assignment_group', current.getValue('assignment_group'))
incident.setValue('assigned_to', current.getValue('assigned_to'));
incident.setValue('priority', current.getValue('priority'));
incident.setValue('impact', current.getValue('impact'));
incident.setValue('caller_id', current.getValue('caller_id'));
incident.setValue('urgency', current.getValue('urgency'));
incident.setValue('business_service', current.getValue('business_service'));
incident.setValue('cmdb_ci', current.getValue('cmdb_ci'));
incident.insert();
Familiarity tells a Snow Developer, hey, that is simple, it's inserting this and that into an incident. Yet, as a reader unfamiliar with to code (say an admin), How would that admin interpret what's going on there? How would one, then, decipher what newRecord() means, why after newRecord() there is another insert(). What does new GlideRecord('incident') do, and more importantly, why after creating a new GlideRecord, it must be followed by newRecord, and finished with insert. This is despite those 15 lines of code having been written as cleanly as can be using a GlideRecord. What if this was inside more code, or, what if there were another 10 fields to set? Even while knowing code, a developer must read all lines to know that an insert is taking place. Time was wasted reading details that can be abstracted away.
Because implementation details there must be a step by step interpretation of the code, rather than stating what it is that needs to happen. By telling the compiler what to do step by step an array of maintenance issues arise. Be it scalability, increased interpretations times, a larger code base, loss of at-a-glance reading, duplication by need, etc. The simplicity of line by line statements can quickly become a hindrance.
Below is a code snipped using layers of abstraction similar to what we currently use in our environment.
A utility class to "clone" fields. Note that there is no support for journalFields in this example
var glideVerter = function glideVerter(params) {
var source = params.source;
var target = params.target;
var fieldMap = params.fieldMap;
fieldMap.forEach(function fillInValues(item) {
target.setValue( item.target, source.getValue(item.source));
});
return target;
};
In the spirit of catchy names, the function is named glideVerter. It basically loops through any object and sets the values of any other object based on a list of fields provided way of a map. It is a unary function that accepts an Object Literal to forgo remembering argument order, while also allowing for dynamically calling the function by any global callback function. Within the object's keys are source[GlideRecord], target[GlideRecord] and fieldMap[Array[Object]]
Array of Data Dictionary
var newIncidentFields = [
{source: 'short_description', target: 'short_description'},
{source: 'description', target: 'description'},
{source: 'assigned_to', target: 'assigned_to'},
{source: 'assignment_group', target: 'assignment_group'},
];
The "dictionary" is a collection of fields to pull from the source and a target to set in the destination record.
Used as such:
var cloneRecordParams = { source: current, target: stageNewRecord('incident'), fieldMap: newIncidentFields};
var newRecord = glideVerter(cloneRecordParams);
insertRecord(newRecord);
The utility function glideVerter has replaced every line of code ever needed to set values from one record to another. The impact on the size of the code base is dramatic. It's scalability excellent, and it's simplicity, though using advanced techniques, rather easy for new developers to understand.
Hopefully, this sparks some creativity to help all of us feed from one another. Advanced concepts and code structure should always aim at read-ability. 🙂
- 231 Views