ServiceNow CAD study materials
ServiceNow CAD study resources

ServiceNow CAD common mistakes

ServiceNow CAD · Understand the traps and how to avoid them.

Mistake 1

Starting to build tables, roles and scripts before creating (and selecting) a scoped custom application, so everything lands in the global scope.

Candidates want to see something working quickly and skip the design/scope step; later questions about cross-scope access, application-scoped record visibility, and 'Accessible from' settings then become guesswork because their mental model was built in global.

What to do instead

Before adding any record, create the app (System Applications > Studio or New Application), confirm the application picker in the header shows that scope, and explicitly walk through which records need to live in the scope vs. be shared from global.

Mistake 2

Using a String field where a Reference field is correct, or writing a display name into a reference field (and comparing a reference field to a name in a query) instead of the 32-character sys_id.

Candidates design from what the user sees on the form, not from what is stored in the column; the form shows the display value, which hides the fact that the underlying value is a GUID.

What to do instead

Decide reference vs. choice vs. string at dictionary level first; in scripts set and compare reference fields with sys_id values (or getDisplayValue()/dot-walking for the label), and verify by checking the field value in a list with the display value turned off.

Mistake 3

Assuming that extending a table copies the parent's fields and data into the child, and expecting a query on the child table to return only child records, or expecting a query on the parent to exclude children.

The table hierarchy behaves like inheritance, not duplication: the child inherits dictionary entries and a GlideRecord on the base table also returns records from tables that extend it, which contradicts the intuitive 'separate table' picture.

What to do instead

Model the hierarchy deliberately (base task-style table plus extensions), remember the child stores sys_class_name to identify its class, and always test a hierarchy query to see what it actually returns before answering questions about record counts or filters.

Mistake 4

Enforcing mandatory fields, read-only fields or input validation with UI policies or client scripts and calling it done.

UI policy is the fastest way to get a form to look correct during testing, so it feels like the natural enforcement tool; candidates forget it runs in the browser only and can be bypassed by list edit, imports, REST, or a user with browser tools.

What to do instead

Split the requirement: presentation and dynamic form behaviour on the client (UI policy/client script), and enforcement on the server (dictionary mandatory/read-only, data policy for import/list edit, business rule or ACL for true security).

Mistake 5

Expecting an onChange client script to run when the form loads, or calling g_form.getValue() on a reference field and treating the result as the display name.

The onChange handler naturally looks like it covers 'the value whenever it changes', and the form shows a friendly name so the returned sys_id looks like a bug rather than the documented behaviour.

What to do instead

Use onLoad for initial state and call the same logic explicitly (or check isLoading) instead of relying on onChange; use getReference()/getDisplayValue() to read the label from a reference field and getValue() only when the sys_id is genuinely what you need.

Mistake 6

Writing g_form or other GlideForm calls inside a business rule or Script Include that runs on the server, or trying to use GlideRecord directly inside a client script.

Candidates memorise one API surface and apply it everywhere, and both environments 'look like JavaScript', so the environment boundary is not top of mind during a timed question.

What to do instead

Anchor each answer to where it executes: server = GlideRecord/current/GlideSystem, client = g_form/g_user/g_scratchpad, and the bridge is GlideAjax calling a client-callable Script Include. State the environment out loud before choosing an API.

Mistake 7

Believing server-side changes persist unconditionally: calling current.update() inside a before business rule, or setting current.field in an after business rule and expecting the record to be saved.

The 'before' and 'after' timing distinction is easy to read but easy to forget under pressure, and update() inside a before rule looks harmless while actually causing duplicate saves or recursion.

What to do instead

Use before rules for validation and for changes to current (they persist automatically without update()); use after rules when you need a separate GlideRecord query plus setValue()/update() for related records, and add recursion guards (workflow flag, query condition) whenever a rule updates its own table.

Mistake 8

Treating a role listed on an ACL as sufficient access, or assuming ACLs on the table grant field-level access, without checking the most-specific-match evaluation and the security_admin elevate requirement.

Roles and ACLs are usually studied as two separate lists rather than as a resolution order, so candidates answer what 'should' be allowed instead of tracing table.field > table.* > . and the deny-by-default behaviour.

What to do instead

Learn the ACL resolution order and practise it: create a field-level ACL that conflicts with the table ACL, impersonate a user with only the lower role, and use session debug / the ACL debug tools to see which ACL actually decided the outcome; remember security_admin must be elevated to edit ACLs.

Mistake 9

Moving records between instances with update sets while the wrong application scope is selected, or deploying a scoped app without its application files, so the target instance has broken references or missing scripts.

Update sets silently capture whatever scope is active, and scoped applications have their own deployment path (application repository/publish), so a candidate who treats 'update set' as a universal transport misses that scoped app metadata travels differently.

What to do instead

Check the application picker before every edit, verify the update set contains the expected records, use the scoped app's own publish/install path for whole applications, and plan deployment order (dictionary/tables before data, roles before ACLs).

Mistake 10

Assuming integration is symmetric: trying to build an inbound endpoint as an outbound REST/SOAP Message record, or forgetting that inbound calls run as a user and are subject to ACLs and web-service access.

Both directions use the word 'integration' and the outbound REST Message module is the one candidates find first; the inbound permission model (authenticated user, ACLs, scripted REST API resources) is easy to skip when focusing on the payload.

What to do instead

Separate the two patterns explicitly: outbound = REST Message + REST step/method; inbound = Scripted REST API (or Import Set + transform map for bulk data). For inbound, define the authentication, the user the call runs as, and verify the ACLs that user needs.