Service Catalog Best Practices in ServiceNow

Crossfuze Service Catalog Example

The goal of this article is to document “Good Practices” that worked well for me through my years of working with numerous clients in creating or enhancing their Service Catalogs. I have divided my list between things that the Business/Project Management should be considering, as well as what the Developers should be doing.

Project Management Best Practices

  1. Strategy
    • Determine your Goals. For most companies this means determining what items will demonstrate the most value to the Business in the quickest fashion
    • Adopt the Agile (iteration based) model. No need to get every feature for every item out on the first pass/iteration. Slow and steady (stability) wins this race.
    • In keeping with the previous point, identify 5 or so Services/Items that are critical to the success of the project/initiative and work on tackling these first; do not try and tackle every service before go-live (or within 1 iteration).
    • Get Buy-in from the C-Level, key stakeholders in the Business, and your primary users (people who will use these items frequently)
    • Always try to keep it simple (think about the Amazon ordering approach) and go with Out Of Box when possible. Yes a Catalog guru can many times make the near-impossible possible, but this usually means it’s complex and will result in manageability issues later.
    • Identify Key Performance Indicators (KPIs) early. If these are not built in (SLAs, Metrics, Surveys, etc.) at the beginning then the information is lost until the appropriate tracking measures are put in place
  2.  Design
    • Design it right the first time, many users will not come back a second time to order if it’s too complicated
    • Keep it simple, ask only questions (via variables) that need to be asked. Don’t just copy a form/item from the old system that was created 10 years ago
    • Make sure the right people are in the room (e.g. Business Analysts, Fulfiller SMEs, etc.) when defining what should be in an item. The key here is to make sure that the items will contain the essential information needed to deliver the requested service
    • Navigation: Consider how users will “find” your items. The two common options are Search (user searches for keywords of an item), or Category “rummaging” (user clicks through multiple category, subcategory sets to find the item). Of the two Search is usually the best choice
    • Don’t ask questions that are already available to you via other places in the system (CMDB, User/group records, etc.)

Developer/Technical Best Practices

  1. Naming
    • Think about the End User experience first when naming your catalog items
    • Descriptions: These aren’t optional. Use Terminology that users know; do not use a bunch of IT jargon that only the cool nerds in the corner use to describe things
  2.  Workflows
    • Avoid combining workflows for multiple items (unless it’s a generic item): sooner or later you will need to break out different paths for different items which becomes a mess if its all the same workflow. Instead, once you have your base workflow, copy/rename it for all the items that will use it; that way you can modify each of them later as the requirements change
    • Double check your code in workflow scripts: if you find an error later after a lot of orders come in, there is no way to force these orders to use a “new/fixed” workflow and thus they may all need to be cancelled and re-ordered (note that the Crossfuze Catalog Turnkey provides an easy method to re-order an item) depending on the type/size of the defect
    • Be careful when using the “Join” utility/activity: this allows the workflow to wait for multiple/simultaneous activities to complete before continuing. If there are “conditional” tasks that never execute (using IF condition activities) then the workflow Join will keep the workflow in a “stuck” state. Explaining this will take a full article which I will be creating soon.
  3.  Variables
    • Name your variables based on a Standard (e.g. same name as the Question with all lowercase and underscores, no special characters except underscores)
    • Keep them short and to the point, nothing worse than having to script on a field thats a sentence (e.g. g_form.getValue(‘what_is_the_best_phone_number_to_call’))
    • Put the important/required fields at the top of the form whenever possible
    • If a question you are asking is somewhat vague, err on the side of user experience; add a tooltip, help tag, or help text
    • Order your Variables by factors of “10”, (10,20,30…), this allows you to put new variables “in-between” in the future without needing to re-order all of the variables
    • Group commonly used variables (such as a header or footer section that has common fields like First Name, Last Name, Requested For, or comments) into Variable Sets for re-use
    • For Reference fields use the ref_auto_completer Attribute in order to define the searchable/visible fields to auto complete:  (ref_auto_completer_AJAXTableCompleter,ref_ac_columns=…)
    • Avoid “Checkbox” variables whenever possible, they are not currently supported by the g_form API therefore you can’t easily manipulate/validate them
    • Avoid “List Collector” (Slushbucket) variables except when absolutely necessary (complex use cases), as these are also not supported by the g_form API and are therefore very hard to programmatically customize. Note: If you still need to use it, here are ways to workaround this limitation using the ServiceNow Guru article found HERE.
    • Avoid “Nesting” containers to give the illusion of 3 or more variables per line. While this can be done safely with 2 columns (OOB), nesting the containers to get 3 or more gets tricky and can cause some weird displaying of variables on mobile devices.
  4. Catalog UI Policies
    • Prefer these over Catalog Client Scripts when it comes to showing/hiding or making variables mandatory as they are more efficient unless your writing scripts in them
    • Make an effort not to have a lot of UI Policy Actions that apply when the condition is EMPTY (running on load), the more fields the system has to go through to Hide, etc. the longer the form takes to load
    • Within Catalog UI Policy Scripts or Client Scripts use the “.setDisplay(‘variable’, false)” rather than “.setVisible(‘variable’, false); this will allow you to reclaim the space that otherwise would be a blank gap in the form
    • When possible, avoid adding the same variable to multiple UI Policies (especially if the conditions could both be true at the same time). If the need does arise, then go ahead and do it but make sure that the Policies have a different “Order” (Lowest order evaluated first)
  5. Catalog Client Scripts (CCS)
    • Validation: ServiceNow provides very little validation of values. So make sure to validate your fields (if its a date, make sure they cant choose yesterday – unless desired, if its a phone number or social security number than validate it contains the right number of characters, etc.)
    • Auto-Populate: If you can automatically populate variables based on information you already know about the user, then do it; it makes everyone’s life easier and keeps your users coming back
    • Auto-Clear: When populating variables based on other variable answers (e.g. populating first/last name based on selected user in reference), make sure to auto-clear these fields if the original field is cleared (especially a good practice when some of these variables may be hidden and still contain “old” data)
    • Use Callbacks: When using AJAX or GlideRecord lookups within CCSs, make sure to use callbacks otherwise the page will “hang” (only exception is onSubmit CCSs as these should wait for a result)
  6. Consider Reporting requirements
    • Reporting on requested items is not pretty. Even once a “Database View” has been created, the best you will get is a new line for every variable/value. One workaround to this behavior is adjust your workflow/scripts accordingly (putting all vars/values into the RITM description at the end)
  7. Order Guides / Wizards
    • Use as a Last Resort; they are difficult to work with and cause a lot of confusion in that they are merely a way to “bundle” separate items and only group by the “Request” which the user doesn’t understand/know about
    • If you do need to go this route then consider the End User and Fulfiller experience, they will be seeing/approving/fulfilling each item as if it was its own, that means that you may have 5 separate workflows running that are spitting out 5 separate emails and approvals and tasks.
  8.  Macros
    • Use Macros (yes that is a variable type) when necessary in order to get good looking HTML loaded (for disclaimers, notes, etc.) in your item
    • Keep them generic, sometimes this means loading data from a lookup table, by setting it up like this you never have to touch the macro once its setup to pull from that table, and it can then be re-used for different items
  9. Generic Catalog Items
    • Don’t be afraid to use them (especially at the beginning of your catalog rollout). These can route to the Service Desk for further triage and give your users the appearance that there are many more items in your catalog (merely create one, then copy it and change the name and descriptions) which can help their overall perception of the catalog and ServiceNow as a whole

One thing to note, as I realize this is a lot of things to remember; keep in mind that at Crossfuze we have developed a Service Catalog turnkey that does a number of these things for you OOB, please contact us if you are interested in learning more.

 

Have I missed anything? Please respond in the comments below to let me know things that are in your list of good practices.

Workflow Driven Catalog UI Policies

Recently I was working with a client on a large Service Catalog project. One of the requirements was that they needed to have specific variables to be visible/required on catalog tasks based on where the item was within the workflow and based on answers that were given to other variables. There are obviously many ways to tackle this; at a very basic level you can choose to show/not show these variables via the slushbucket on the task activity. While this does work in most cases, in more complex scenarios like this, it does not cover the capability to conditionally show/hide certain variables based on the values of other variables. In the past, I have tackled these types of issues by using UI Policies that looked for specific text/data in order to “figure out” where the item was at in the workflow. While this approach can work in certain circumstances, I wanted a more robust method of controlling what fields were required/visible/mandatory. The result was “Workflow generated UI Policies” that can be called by any item (using a Catalog Client Script), with a back-end AJAX Script Include that queries the item’s Workflow context, then grabs the UI Policies (exist as a scratchpad array) off of the scratchpad, figures out which one is applicable, then sends the payload back to the client script.

Here is what the Workflow Run script contains:

//set UI Policy scratchpad
workflow.scratchpad.ui_policies = [];
workflow.scratchpad.ui_policies.push(
		{
			"applies_to": workflow.scratchpad.task_sys_id,
			"readOnly": ['group'],
			"writeable":['first_name','last_name','laptop'],
			"mandatory":['laptop'],
			"visible":[''],
			"hidden":['assigned_to']
		});

Here’s the Script Include:

var CatalogItemAJAXUtil = Class.create();
CatalogItemAJAXUtil.prototype = Object.extendsObject(AbstractAjaxProcessor, {

	getWorkflowScratchpad : function (){
		var req_item =  this.getParameter('sysparm_req_item');
		var record_id = this.getParameter('sysparm_record_id');
		var req_item_policies;
		var matched_policies = '0';

		//get the workflow script include helper
		var workflow = new Workflow();

		//get the requested items workflow context
		var item = new GlideRecord('sc_req_item');
		item.get(req_item);
		var context = workflow.getContexts(item);

		//make sure we have a valid context
		if (context.next()) {
			gs.log('++CatalogItemAJAXUtil: Got Policies:' + new JSON().encode(context.scratchpad.ui_policies));
			//this has already been instantiated as an array/collection, we can use it directly
			req_item_policies = context.scratchpad.ui_policies;

			//loop through all policies, find out which one's apply to this record
			matched_policies = this.getMatchingPolicies (req_item_policies,record_id);
			//gs.log('++Policy Applies to: ' + req_item_policies.applies_to +' Record we were on: ' + record_id);

		} else {
			//no workflows found for this item; ignoring...
		}
		if (matched_policies) {
			gs.log('++Returning UI Policies: ' + matched_policies + ' for Record: ' + record_id);
		} else {
			gs.log('++No Matching UI Policies found for Record: ' + record_id);
		}

		//convert to JSON String before sending
		return new JSON().encode(matched_policies);
	},

	getMatchingPolicies : function (policies,record_id){
		var item_policies = [];
		for (var i = 0; i < policies.length; i++) {
			var policy = policies[i];
			if (policy.applies_to == record_id) {
				//gs.log('++ Found matching UI Policy for this record');
				item_policies.push(policy);
			}
		}
		return item_policies;
	},

	type: 'CatalogItemAJAXUtil'
});

Finally, here is what the Catalog Client Script looks like:

function onLoad() {
	var ga = new GlideAjax('CatalogItemAJAXUtil');
	ga.addParam('sysparm_name','getWorkflowScratchpad');
	ga.addParam('sysparm_req_item',g_form.getValue('request_item'));
	ga.addParam('sysparm_record_id',g_form.getUniqueValue());
	ga.getXML(ScratchpadParse);

}

function ScratchpadParse(response) {
	var answer = response.responseXML.documentElement.getAttribute("answer");
	jslog('++ AJAX Answer was: ' + JSON.stringify(answer));
	var workflow_ui_policies = JSON.parse(answer);
	jslog('++ Scratchpad UI Policy Obj was: ' + JSON.stringify(workflow_ui_policies));

	//loop through and apply each UI Policy

	for (var i = 0; i < workflow_ui_policies.length; i++) {
		var policy = workflow_ui_policies[i];
		if (policy == '0' || policy == null){
			jslog('++ Returned UI Policy is not Valid for this record');
			continue;
		}

		jslog('++ Running Scratchpad Policies ' + policy['readOnly'] + ' String: ' + JSON.stringify(policy));
		setPolicyValues(policy['readOnly'],'readOnly');

		//jslog('++ReadOnly:' + policy['readOnly']);
		setPolicyValues(policy['writeable'],'writeable');
		//jslog('++writeable:' + policy['writeable']);

		setPolicyValues(policy['mandatory'],'mandatory');
		//jslog('++mandatory:' + policy['mandatory']);

		setPolicyValues(policy['visible'],'visible');
		//jslog('++visible:' + policy['visible']);

		setPolicyValues(policy['hidden'],'hidden');
		//jslog('++hidden:' + policy['hidden'])
	}
}

function setPolicyValues(policy_fields,action){
	for (var i = 0; i < policy_fields.length; i++) {
		var field = policy_fields[i];

		if (action == 'readOnly') {
			g_form.setReadOnly(field, true);
		} else if (action == 'writeable') {
			g_form.setReadOnly(field, false);
		} else if (action == 'mandatory') {
			g_form.setMandatory(field, true);
		} else if (action == 'visible') {
			g_form.setVisible(field, true);
		} else if (action == 'hidden') {
			g_form.setVisible(field, false);
		}
	}
}

It is these 3 pieces of code that make up the Custom Workflow UI Policy process. Once this is setup, you can have multiple custom/scratchpad UI Policies in your workflow and then based on the Record’s ID (sys_id), it will dynamically apply the corresponding UI Policy for that record.

Ok, that’s it for now, please let me know if you have any questions.