The first fifteen minutes of a ServiceNow interview almost always cover the same ground: reference qualifiers, sys_id, how you update records from a script, the difference between the rules that run in the browser and the rules that run on the server, record producers, and update sets. Admin, developer and support roles all start here. The answers below are the ones a panel expects at two or three years of experience, with the scripts and the caveats that separate a candidate who has configured these things from one who has read about them. Twelve questions, answered in the depth the panel is actually listening for.
Reference qualifiers
What is a reference qualifier?
A reference qualifier is a filter applied to a reference field so that the lookup list shows only the records that make sense in that context.
A reference field stores the sys_id of a record on another table. Without a qualifier, the magnifying glass on that field shows every record on the referenced table. On a 40,000 row User table, that is not usable. The reference qualifier restricts the result set before it reaches the user.
It is configured on the dictionary entry of the field, not on the form. Open the field, right click the label, choose Configure Dictionary, and look at the Reference Specification section. The qualifier you set there applies everywhere the field appears: the form, the list editor, the Service Portal, a record producer variable that points at the same table, and the reference lookup used by an inbound integration.
Two points that come up as follow-ups. First, a reference qualifier is a usability and data quality control, not a security control. It filters the list; it does not stop someone writing a sys_id into that field through a script or a web service. If the requirement is "this user must not be allowed to select that record", you need an ACL or a data policy behind it. Second, the qualifier runs on the server, which is why a badly written one slows the form down for everybody.
What are the different types of reference qualifier?
There are three, and the panel usually wants to hear all three with the reason you would pick each.
| Type | Where it is defined | Use it when |
|---|---|---|
| Simple | Condition builder on the dictionary entry | The filter is a fixed condition, for example Active is true and Type is Hardware |
| Dynamic | A record in Dynamic Filter Options, backed by a script include | The same filter is needed on several fields or tables, for example "users in my groups" |
| Advanced | An encoded query string or a javascript: call on the dictionary entry |
The filter depends on other values on the current form |
Simple is the condition builder. It is stored as an encoded query and it is the right answer whenever the condition does not change. Most teams over-script this one.
Advanced accepts either a raw encoded query or a javascript: expression that returns an encoded query:
javascript:new IncidentRefQualifiers().activeCIsForCompany();
Everything after javascript: runs on the server, and the current object carries the values sitting on the form, including unsaved ones. That is what allows the Configuration item list to narrow as soon as the Company field changes. For the list to refresh when that other field changes, add the dictionary attribute ref_qual_elements=company to the reference field. Candidates forget this attribute constantly and then report that "the qualifier only works after save".
Dynamic is the reusable one. You create a record in Dynamic Filter Options, point its script at a script include, and then select that filter on any dictionary entry that references the same table. Here is the pattern end to end.
The Dynamic Filter Option record:
- Label:
Members of my groups - Table:
Global - Field type:
reference - Referenced table:
User [sys_user] - Script:
new UserReferenceQualifiers().membersOfMyGroups(); - Active: true
The script include it calls, with Client callable left unchecked because this runs server side only:
var UserReferenceQualifiers = Class.create();
UserReferenceQualifiers.prototype = {
initialize: function() {},
// Returns an encoded query for the User [sys_user] table:
// only users who share at least one group with the logged in user.
membersOfMyGroups: function() {
var myGroups = this._groupsOf(gs.getUserID());
if (myGroups.length === 0)
return 'sys_idIN-1'; // no groups, so show an empty list
var users = [];
var member = new GlideRecord('sys_user_grmember');
member.addQuery('group', 'IN', myGroups.join(','));
member.addQuery('user.active', true);
member.query();
while (member.next()) {
users.push(member.getValue('user'));
}
if (users.length === 0)
return 'sys_idIN-1';
return 'sys_idIN' + users.join(',');
},
_groupsOf: function(userId) {
var groups = [];
var gm = new GlideRecord('sys_user_grmember');
gm.addQuery('user', userId);
gm.query();
while (gm.next()) {
groups.push(gm.getValue('group'));
}
return groups;
},
type: 'UserReferenceQualifiers'
};
On the dictionary entry you then set Use reference qualifier to Dynamic and pick Members of my groups from the list. Any other reference field pointing at sys_user can select the same filter, which is the whole argument for the dynamic type.
Three things to say about that script if you are asked to critique it. The method returns a string, never a GlideRecord, because the platform expects an encoded query. The sys_idIN-1 fallback is deliberate: returning an empty string would remove the filter and show every user, which is the opposite of what the requirement asked for. And a dynamic filter is meant to be reusable across tables and fields, so it should not depend on current; the moment your filter needs another field from the form, that is an advanced qualifier, not a dynamic one.
This is where our trainer sees the gap between syntax and execution. Candidates can write the script include. Ask them where it runs, what data it can see, and how many times it runs when a user opens the lookup, and the answer stops.
Platform version and record identity
How do we know which version of ServiceNow we are using?
There are four places, and a good answer names more than one because they tell you different things.
- System Diagnostics > Stats > Stats, or the
/stats.dopage directly. This shows the build name, build date and build tag. The build tag is the precise answer, because it carries the release family, the patch and any hotfix. - System Diagnostics > Upgrade History. Each row records an upgrade, the release it moved to, and when it ran. This is what you check when something broke after a weekend.
- A background script.
gs.info(gs.getProperty('glide.buildtag'));returns the build tag. Theglide.warproperty returns the WAR file name, which carries the same release and patch information. - The sys_upgrade_history table, queried directly, if you want the history as data rather than as a page.
Be clear on release versus patch. ServiceNow names family releases alphabetically, one after another, Tokyo then Utah, Vancouver, Washington DC, Xanadu, Yokohama, Zurich, and each family receives patches and hotfixes on top. Saying "we are on Washington DC" answers half the question. An interviewer asking this is usually testing whether you have ever sat in an upgrade window, because that is the only time the patch level matters to you.
What is a sys_id?
A sys_id is the unique 32 character hexadecimal identifier that every record on every table in ServiceNow carries. It is the primary key of the platform.
The properties worth stating:
- It is generated by the platform on insert and it does not change for the life of the record.
- It is unique across tables, not just within a table, because it is a globally unique identifier.
- It is what a reference field actually stores. The Caller field on an Incident holds a sys_id; the user's name is only the display value resolved at read time.
- It is what appears in the URL of any record, as
sys_id=on the form URL. - Baseline records shipped by ServiceNow carry the same sys_id on every instance, which is why an update set moving a customised out of the box record lands on the right record in the target instance rather than creating a duplicate.
The follow-up is usually about reading it in a script, and it is worth being exact:
var gr = new GlideRecord('incident');
if (gr.get('number', 'INC0010001')) {
gs.info(gr.getUniqueValue()); // sys_id of the incident
gs.info(gr.getValue('caller_id')); // sys_id of the caller
gs.info(gr.caller_id.getDisplayValue()); // the caller's name
gs.info(gr.caller_id.email); // dot-walk through the sys_id
}
getValue() on a reference field returns the sys_id as a string. getDisplayValue() returns the text the user sees. Confusing the two is the single most common cause of a comparison that silently never matches, and it is a favourite debugging question.
Updating records from a script
If I wanted to update fields on a table using a script, but preserve the last updated person's name on the record, how can that be done?
Two methods, and the panel is checking whether you know which one does which, because candidates routinely name only setWorkflow(false) and think it covers both.
autoSysFields(false) is the one that preserves the audit stamp. It tells the platform not to touch the system fields on this operation, so sys_updated_by, sys_updated_on and sys_mod_count keep their existing values. The Updated by on the record continues to show the person who last made a real change, not the account running your script.
setWorkflow(false) is the one that stops the automation. It disables business rules, workflows, notifications and the script engines for that operation. It has nothing to do with the system fields. Use it on a bulk data fix so that ten thousand updates do not fire ten thousand emails and reopen every SLA.
Both are called on the GlideRecord before the update, and a mass data correction usually wants both:
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('category', 'inquiry');
gr.query();
while (gr.next()) {
gr.setValue('category', 'request');
gr.autoSysFields(false); // keep sys_updated_by, sys_updated_on, sys_mod_count
gr.setWorkflow(false); // suppress business rules, notifications, workflow
gr.update();
}
The important limitation, and the reason this question is asked at all: autoSysFields() is not available in a scoped application. The scoped GlideRecord API does not expose it. setWorkflow() is available in scope, but the system field suppression is not. If you genuinely need to preserve the Updated by inside scoped work, the practical route is to run the correction from the global scope, as a background script or a fix script in global, and to raise it in the design review rather than hunting for a scoped workaround that does not exist.
Two cautions to add unprompted, because they make the answer sound like experience rather than recall. setWorkflow(false) does not turn off access controls; it turns off automation, so a script that was going to fail on permissions still fails. And suppressing business rules suppresses the ones you wanted as well, including anything that maintains a related record, so a data fix that runs with it needs a verification query afterwards rather than a hope.
Data policies, UI policies and dictionary overrides
What is Data Policy?
A Data Policy is a server side rule on a table that makes fields mandatory or read only when a condition is met. It is configured under System Policy > Rules > Data Policies, and the individual Mandatory and Read only rules sit in a related list on the policy.
The defining characteristic is where it is enforced. The check happens when the record is written to the database, so it holds regardless of how the write arrived: a form save, a list edit, an import set transform, an inbound REST or SOAP call, or a server side script. A record that violates a mandatory data policy is rejected at the point of insert or update.
Two checkboxes on the policy are worth naming. Apply to import sets extends the policy to records coming through a transform map, which is how teams stop an integration loading incidents with no Short description. Use as UI Policy makes the platform enforce the same conditions on the form as well, so the user sees the red mandatory marker instead of only getting an error on save. There is also Reverse if false, which undoes the rule when the condition stops being true.
What a Data Policy cannot do: it cannot hide a field, it cannot change field values, and it cannot run a script. Mandatory and read only are the only two actions available.
Difference between UI Policy and Data Policy?
Both control mandatory and read only behaviour, so candidates treat them as interchangeable. They are not, and the distinction is entirely about which write paths they cover.
A UI Policy only exists on a form. It is delivered to the browser and evaluated there. Close the form, and the policy is not part of the picture. Anything that writes to the table without rendering that form, an import, a web service, a background script, a business rule on another table, never sees it.
A Data Policy exists at the data layer. It is evaluated on the server every time the record is written, which means it covers the import, the web service and the background script as well as the form.
| UI Policy | Data Policy | |
|---|---|---|
| Runs | In the browser, on the form | On the server, on insert and update |
| Form save | Enforced | Enforced |
| List edit | Not enforced by the form policy | Enforced |
| Import set / transform map | Not enforced | Enforced when Apply to import sets is ticked |
| Inbound REST or SOAP | Not enforced | Enforced |
| Background or server script | Not enforced | Enforced |
| Actions available | Mandatory, Read only, Visible, plus Execute if true and Execute if false scripts | Mandatory and Read only only |
| Can be reused on the other side | No | Yes, through Use as UI Policy |
The practical rule most teams land on: use a Data Policy when the requirement is about data integrity and must hold on every path into the table, and a UI Policy when the requirement is about the experience on the form, which includes anything involving showing and hiding fields or running client script.
Our trainer's version of the test is not the definition. It is "your integration is loading records that break a rule the form enforces; what did the developer configure and what should they have configured". A candidate who understands where each one runs answers that in one sentence.
What is Dictionary Override?
A Dictionary Override changes the behaviour of a field inherited from a parent table, on one child table only, without touching the parent or the other children.
ServiceNow tables extend each other. Incident, Change Request, Problem and Catalog Task all extend Task, and they all inherit Task's fields. If you edit the dictionary entry of short_description on Task, the change lands on every table that extends Task. A Dictionary Override lets you make the change on Change Request alone.
The attributes you can override include the default value, mandatory, read only, the reference qualifier, the calculated value and the dictionary attributes. You create it from Configure Dictionary on the field, in the Dictionary Overrides related list, or directly on the child table's dictionary entry.
A worked example. The requirement is that Description is mandatory on Change Request, because no change should go to CAB without one, but it stays optional on Incident so that the service desk can log a call in ten seconds. Editing the Task dictionary would make it mandatory everywhere. So you create a Dictionary Override on the Change Request table for the description element, tick Override mandatory, and tick Mandatory. Incident is untouched.
The reason interviewers like this question is that it separates people who have extended a table from people who have only configured one. The follow-up is often "why not just use a UI policy on Change Request", and the answer is that the override is a dictionary level change that holds across every form, list and view of that table, while the UI policy holds only where it is rendered.
Record producers and catalog items
What is Record Producer?
A Record Producer is a type of catalog item that creates a record on a table you nominate, instead of creating a request.
The user sees a catalog style page with variables rather than the native form. On submit, the values are written to a record on the target table. It lives in the Service Catalog, it uses the same variables, variable sets, catalog client scripts and catalog UI policies as any other catalog item, and it is controlled by the same user criteria.
Mapping the variables to fields is done in one of two ways. A variable can carry a Map to field setting that points at a column on the target table, or the producer's script can do it explicitly. In that script you have two objects: current, which is the record being created, and producer, which holds the variable values.
// Record producer script on a producer whose table is Incident [incident]
current.caller_id = producer.affected_user;
current.category = 'hardware';
current.short_description = 'Laptop issue: ' + producer.issue_type;
current.description = producer.details;
current.impact = 2;
current.urgency = producer.is_blocking == 'true' ? 1 : 3;
The standard use is guided intake. Rather than exposing the Incident form to an end user with thirty fields they do not understand, you give them four plain language questions in the portal and let the producer set the category, impact and assignment group correctly behind the scenes.
Answer this one with a table name and a requirement attached. Memorised definitions collapse the moment the panel says "give me a real project example", and this is the question where that happens most often.
Difference between Catalog Item and Record Producer?
Both are ordered from the Service Catalog and both are built from variables. The difference is what exists after submit.
| Catalog Item | Record Producer | |
|---|---|---|
| Record created | A Request, with one Requested Item per item, and Catalog Tasks under it | One record on the table you configured, for example Incident or HR Case |
| Target table | Fixed, the request tables | Any table you choose, including custom tables |
| Fulfilment | Driven by the flow or workflow attached to the item | Whatever process already exists on the target table |
| Approvals | Part of the request process, on the item or the request | Only if the target table's own process has them |
| Cart and quantity | Supported, including price and quantity | No cart, no price, one record per submission |
| Typical use | Ordering something: a laptop, access, a new joiner kit | Reporting something: an incident, a complaint, an HR query |
The shortest way to separate them in an interview: a catalog item is a request for fulfilment, and a record producer is a friendlier front door onto an existing table.
What the interviewer is actually checking
- Whether you know where code runs. Reference qualifiers, data policies and UI policies are all questions about server side versus client side wearing different clothes. Naming the browser or the server in your answer, unprompted, is what separates the two groups.
- Whether you have caused a problem and fixed it.
autoSysFields(false)andsetWorkflow(false)are only memorable to someone who has once sent a thousand notifications by accident, or destroyed an audit trail on a table an auditor later asked about. - Whether you can name the table. sys_id, sys_user_grmember, sys_update_set, sys_dictionary. Specificity creates credibility, and vagueness at this level makes the panel discount everything you say later.
- Whether you can debug. "Your reference qualifier returns nothing" and "your update set did not reach test" are worth more to a panel than any definition, because they show whether you have an ordered way of narrowing a fault.
- Whether your example survives five levels. Which table, what requirement, why this approach over the alternative, and what was your own part in it. Every fundamentals answer should have one real example attached that holds up to those four follow-ups.
Where to take this next
These twelve questions are the ones that set the panel's expectation for the rest of the round. A thin answer on reference qualifiers or update sets tells the interviewer to pitch the remaining questions lower, and the round rarely recovers from that. So practise them out loud, with a table name and a requirement attached to each, until the example comes before the definition.
If you want to build these answers on real configuration rather than notes, the ServiceNow training programme at RizeX Labs covers this ground on a live instance, with the scripting, catalog and deployment work that the follow-up questions come from.