UI policies are the no-code way to make a ServiceNow form react: show the asset tag field only for hardware incidents, make close notes mandatory on resolution, lock fields once a change is approved. A single policy is trivial. A form with eight policies, two client scripts and an inherited policy from task, all touching the same three fields, is where the support tickets come from: “the field disappears for no reason”, “it’s mandatory but I can’t see it”. Interviewers ask about UI policies to see whether you can reason about that interaction, not whether you can fill in the form.
What a UI policy controls
A UI policy has a table, a condition built in the condition builder, and UI policy actions, one per field, each setting three properties: Mandatory, Visible and Read only. Each property can be True, False or Leave alone. “Leave alone” matters: it’s how a policy avoids having an opinion about a property another policy owns.
The options that change behaviour:
- Reverse if false: when the condition is false, undo the actions (apply the opposite).
- On load: also evaluate when the form loads, not only when fields change.
- Global or a specific View: apply to all form views or one.
- Inherit: apply to tables that extend this one.
- Run scripts: add Execute if true and Execute if false client scripts, for the cases actions can’t express.
Catalog items have catalog UI policies that do the same for variables.
The rules of evaluation
ServiceNow’s documentation sets out how policies combine, and each rule explains a class of bug:
- Order runs lowest to highest. If two policies conflict, the one with the higher order wins, because it’s applied last.
- Child before parent. For inherited policies, the child table’s policies run first, then the parent table’s, each from lowest to highest order, regardless of the order numbers across the two tables. A parent policy at order 100 can therefore override a child policy at 500.
- Conditions are rechecked only on manual changes. If a UI action, a context menu action or the list editor changes a value, UI policies aren’t re-evaluated. A condition is evaluated even if the field it tests isn’t on the form.
- Mandatory beats hidden. A field that’s mandatory and still empty can’t be hidden; the form keeps it visible so the user isn’t blocked by a field they can’t see.
Three policies, one field
Here’s a realistic set for a custom Asset tag field on incident, modelled in Node.js (plain JavaScript reproducing the documented rules, not the platform’s policy engine). Hardware incidents need the tag; resolved incidents lock it; incidents raised through self-service hide it because callers don’t know their tag. All three have Reverse if false ticked, which is the default many people leave on.
// UI policies applied in ascending order; "Reverse if false" applies the opposite of each action.
const policies = [
{ order: 100, name: 'Hardware needs asset tag', reverse: true,
when: (f) => f.category === 'hardware', actions: { visible: true, mandatory: true } },
{ order: 200, name: 'Lock asset tag when resolved', reverse: true,
when: (f) => f.state === 'resolved', actions: { readOnly: true } },
{ order: 300, name: 'Hide asset tag on self-service', reverse: true,
when: (f) => f.contact_type === 'self-service', actions: { visible: false } },
];
function render(form, list) {
const field = { visible: true, mandatory: false, readOnly: false }; // dictionary defaults
const trace = [];
for (const p of [...list].sort((a, b) => a.order - b.order)) {
const hit = p.when(form);
if (!hit && !p.reverse) continue;
for (const [prop, value] of Object.entries(p.actions)) field[prop] = hit ? value : !value;
trace.push(`${p.order}${hit ? '' : ' reversed'}`);
}
// The form won't hide a mandatory field that is still empty.
if (field.mandatory && !form.u_asset_tag && !field.visible) { field.visible = true; trace.push('kept visible: mandatory and empty'); }
return `${field.visible ? 'shown' : 'hidden'}${field.mandatory ? ', mandatory' : ''}${field.readOnly ? ', read-only' : ''} [${trace.join(', ')}]`;
}
const forms = [
{ label: 'hardware, phone', category: 'hardware', contact_type: 'phone', state: 'in_progress', u_asset_tag: '' },
{ label: 'software, phone', category: 'software', contact_type: 'phone', state: 'in_progress', u_asset_tag: '' },
{ label: 'hardware, self-service', category: 'hardware', contact_type: 'self-service', state: 'in_progress', u_asset_tag: '' },
{ label: 'hardware, resolved, tagged', category: 'hardware', contact_type: 'phone', state: 'resolved', u_asset_tag: 'P1000123' },
];
for (const f of forms) console.log(`${f.label.padEnd(27)} ${render(f, policies)}`);
console.log('-- after unticking Reverse if false on the self-service policy --');
policies[2].reverse = false;
for (const f of forms.slice(1, 2)) console.log(`${f.label.padEnd(27)} ${render(f, policies)}`);hardware, phone shown, mandatory [100, 200 reversed, 300 reversed]
software, phone shown [100 reversed, 200 reversed, 300 reversed]
hardware, self-service shown, mandatory [100, 200 reversed, 300, kept visible: mandatory and empty]
hardware, resolved, tagged shown, mandatory, read-only [100, 200, 300 reversed]
-- after unticking Reverse if false on the self-service policy --
software, phone hidden [100 reversed, 200 reversed]The first and last rows behave as designed. The second is the bug report: a software incident from the phone channel shows the asset tag field. Policy 100 correctly hides it (reversed, because the category isn’t hardware), but policy 300’s condition is also false, so its reversal applies the opposite of “visible = false”, which is “visible = true”, and as the higher order it wins. Nobody wrote a rule saying “show the asset tag on phone incidents”, yet one exists.
The third row is the other classic. On a self-service hardware incident, policy 100 makes the field mandatory and policy 300 tries to hide it. Because it’s mandatory and empty, the form keeps it visible anyway, so the self-service policy appears not to work, and if it could hide the field, the user would be blocked by a mandatory field they can’t see.
After unticking Reverse if false on the self-service policy, the software incident hides the field as intended: the policy now only acts when its condition is true.
Reverse if false is an action too
The lesson from the model: a policy with Reverse if false has an opinion in both directions. It isn’t “do X when the condition holds”; it’s “do X when it holds, and do not-X otherwise”. For a policy that owns a field outright, that’s exactly right (close notes are mandatory when resolved and optional otherwise). For a policy that adds an exception on top of another policy’s behaviour, like hiding a field for one channel, Reverse if false turns the exception into a competing rule.
Two ways to keep it straight:
- One policy per field property, with the full condition. Instead of separate “show for hardware” and “hide for self-service” policies, write one: Visible when category is hardware and contact type isn’t self-service. One owner, no ordering puzzle.
- Untick Reverse if false on exception policies, and set the properties they don’t own to Leave alone.
When client scripts touch the same field
The same reasoning applies across mechanisms. If a UI policy sets Asset tag mandatory and an onChange client script calls g_form.setMandatory('u_asset_tag', false), the field’s final state depends on which ran last for that particular event, and the two run on different triggers. That’s fragile even when it happens to work. Decide which mechanism owns each property: UI policies for declarative show, hide, mandatory and read-only behaviour (ServiceNow recommends them over client scripts for these where possible), and client scripts for things a policy can’t express, such as messages, value calculation or calls to the server.
What UI policies can’t do
UI policies change the form in the browser. They don’t run for the REST API, imports, flows, scripts or list edits, and conditions aren’t rechecked after UI actions. So a mandatory field set by a UI policy isn’t mandatory for an integration, and a read-only field isn’t read-only to someone who updates the record any other way. If the rule must hold, back it with a data policy (which can also act as the UI policy through Use as UI Policy on client, so there’s a single definition) or with a before business rule, and keep ACLs for who may write the field at all.
When a form misbehaves, list every UI policy on the table and its parents, plus client scripts, that mention the field. The Field Watcher in the classic form (right-click the field label) can show which scripts and policies touched the field, but the inventory usually reveals the culprit first.
Interview practice
On the change request form, the Backout plan field should be mandatory for normal changes. Users report that it’s sometimes not mandatory when they switch the type from Standard to Normal, and an inherited policy from task is mentioned in the ticket. How do you investigate and fix it?
Answer
I’d start by listing every UI policy that touches Backout plan, on change_request and on task (with Inherit ticked), plus any client scripts that call setMandatory on it, noting each one’s order, condition and Reverse if false setting. The likely culprit is a policy with Reverse if false whose condition is false for normal changes; when reversed, it applies “mandatory = false”. If that policy is on task, it runs after the change request’s own policies (child before parent), so it wins regardless of order. Another candidate is a UI action or script changing the type, since UI policy conditions aren’t rechecked after UI actions.
The fix is to give the property one owner: a single policy on change_request that sets Backout plan mandatory when type is Normal (with Reverse if false, since it owns the property), and set Mandatory to Leave alone on the inherited policy, or untick Inherit if it was never meant for changes. Because a backout plan for normal changes is a real requirement, I’d also add a data policy or before business rule so changes created through the API or a flow can’t skip it, then test switching types in the form and creating a normal change through the API.
Related reading
- ServiceNow interview questions
- ServiceNow multiple-choice questions
- ServiceNow Client Scripts Versus Server Enforcement, on backing form rules with server-side ones
- ServiceNow Table Inheritance and Shared Behavior, for how inherited policies reach child tables
- UI policies (ServiceNow documentation)