Script Includes Are Where Reusable Server Logic Belongs
Reusable server-side logic is one of the clearest dividing lines between a ServiceNow application that grows cleanly and one that accumulates duplicated scripts. Script Includes exist specifically so shared functions and classes can live in one server-side location and be called when needed. That is why the ServiceNow CAD exam expects developers on the ServiceNow certification path to understand scripting as application architecture, not merely JavaScript syntax.
A Script Include does not fire because a record changed. It executes only when another script calls it. That small distinction gives it a very different role from Business Rules, Client Scripts, or flows. It is a library boundary: the caller decides when work should happen, while the Script Include owns how a reusable piece of server logic is performed.
Put shared domain behavior in one place
If three Business Rules calculate the same eligibility condition, the application does not have three implementations; it has one rule that has been copied three times. Those copies will eventually drift. Moving the calculation into a Script Include gives every caller the same behavior and creates one place to fix defects.
This is ordinary software development discipline expressed on the Now Platform. Reuse is not about creating a giant helper file. It is about giving one concept one implementation so that testing, versioning, and change review can focus on a stable contract.
Design around responsibilities, not around a generic utilities bucket
A common anti-pattern is a Script Include named Utils that grows hundreds of unrelated methods. That centralizes code physically without creating a useful abstraction. Prefer cohesive classes or functions that represent a domain responsibility: entitlement evaluation, supplier lookup, assignment policy, date calculation, or integration mapping.
Cohesion makes the API easier to understand. Callers should not need to know which tables a helper happens to query internally. They should ask a method a meaningful question and receive a defined result. When a Script Include has a clear responsibility, it can evolve internally without forcing every caller to change.
Keep method inputs and outputs small and explicit
Passing a full GlideRecord everywhere can couple reusable logic tightly to one calling context. Sometimes that is appropriate, but often a method can accept a sys_id, a small set of primitive inputs, or a clearly documented object and return a Boolean, value, or structured result. Smaller contracts are easier to test and harder to misuse.
Think about complexity as well as readability. The principles behind algorithmic complexity still matter on a platform: a method that loops through thousands of records or issues one query per item can become expensive quickly. Reuse amplifies both good and bad performance because many callers may invoke the same code.
Do not make Script Includes client callable by default
A Script Include can be exposed to client code through GlideAjax when a browser interaction genuinely needs server data. That should be an explicit design choice. Client-callable methods create an externally reachable boundary and therefore need input validation, narrow outputs, and authorization checks appropriate to the data being returned.
If a method is only used by server-side code, leave it server-only. Reducing exposed surface area simplifies security. When client access is required, separate the small client-facing contract from internal helper methods rather than exposing a broad class full of capabilities.
Application scope is part of the API contract
Scoped applications can limit where a Script Include is accessible and can use caller access controls to manage cross-scope use. These settings are not administrative trivia. They define which other applications are allowed to depend on the code and therefore how safely the implementation can change later.
That boundary connects to secure software design. Public APIs create obligations. If multiple scopes depend on a Script Include, changing method names, return structures, or side effects can break other applications. Keep cross-scope contracts deliberate and document the expected callers.
Separate calculation from side effects where possible
Methods that both calculate a result and update several records are harder to reuse safely. A cleaner design often separates pure or mostly deterministic calculations from commands that make changes. The caller can then decide when to persist a result, while the reusable calculation can be tested against many inputs without constructing a full transaction.
ServiceNow development will never be purely functional—database access is central to the platform—but reducing unnecessary side effects still improves predictability. A lookup method should not quietly update data unless that behavior is the explicit purpose of the method.
Return useful errors instead of hiding failure
A reusable method should have a defined failure contract. Decide whether invalid input throws an error, returns a status object, produces an empty result, or logs and continues. Silent catch blocks create difficult incidents because the caller believes the work succeeded while the underlying problem disappears into a log.
Good error information identifies what failed without exposing sensitive data. Integration callers may need a retryable versus non-retryable distinction; UI callers may need a user-safe message; background jobs may need diagnostic context. The Script Include can standardize that behavior instead of letting every caller invent its own interpretation.
Test Script Includes through the behaviors that depend on them
Reusable logic deserves focused tests. The broader habits in JavaScript testing are useful even when the platform’s test mechanisms differ from a standalone JavaScript stack. Exercise normal inputs, boundary conditions, permission-sensitive paths, missing records, and error cases, then verify the flows or rules that depend on the method.
Because many callers can depend on one Script Include, a regression in shared logic has a wider blast radius than a local script defect. That is a reason to make the interface smaller and the tests stronger, not a reason to duplicate code to avoid shared dependencies.
Dependency direction matters when Script Includes call one another. Lower-level data-access helpers can support higher-level domain services, but circular dependencies make behavior difficult to follow and can turn small changes into broad regressions. Keep foundational utilities narrow, keep domain services focused on business meaning, and avoid letting every class call every other class simply because they are available in the same scope. A simple dependency graph is easier to test and easier to protect with caller-access settings.
Caching can help repeated lookups, but it should be introduced only when the lifetime and invalidation rules are understood. A cached reference value that changes rarely may save repeated queries; a cached authorization or state decision can become dangerous when the underlying record changes. Measure the expensive path first, then choose a cache whose scope matches the truth being cached. Performance improvements that return stale business decisions are not improvements.
Naming and documentation are part of the API. Method names should describe the business question or action rather than the implementation detail, and descriptions should explain inputs, outputs, side effects, and security assumptions. This becomes especially important when the class is callable from another scope. A future developer should not need to read the implementation to discover that a method updates records, impersonates a context, or expects a property to exist.
When refactoring duplicated scripts into a Script Include, migrate callers deliberately. Compare outputs before and after, move one call site at a time when risk is high, and remove the old implementation once every caller uses the shared service. Leaving copied logic in place ‘just in case’ defeats the point of centralization because the application again has multiple sources of truth. Reuse pays off only when the shared implementation actually becomes authoritative.
Performance testing should examine call frequency as well as method speed. A helper that completes in ten milliseconds may still be expensive if a list query invokes it thousands of times through another rule. Trace the contexts in which the Script Include is used, identify hot paths, and avoid designs that perform repeated database work for values that the caller already has. Reusable code should reduce duplicated logic, not multiply duplicated queries.
Shared services should also expose metrics when they perform important or expensive work. Execution time, error rate, query volume, and call counts can reveal when a reusable method has become a bottleneck or a hidden dependency for many features. Instrumentation is especially useful after refactoring because it confirms that centralization reduced duplication without creating one overloaded hotspot that every transaction now waits for.
Keep the public method surface intentionally small. Every callable method becomes another contract that future code may depend on. Fewer, clearer entry points give the implementation room to improve without breaking callers.
A Script Include should make the application easier to explain
Developers should be able to trace a requirement from the trigger to a reusable service and understand which component owns each decision. A Business Rule detects the record event, a flow orchestrates a process, and a Script Include evaluates a shared business rule or retrieves data. Each layer is then small enough to reason about.
When Script Includes are designed as cohesive server-side services, they reduce duplication without becoming a junk drawer. The payoff appears months later: fixes happen once, security boundaries are clearer, tests target stable contracts, and new automation can reuse the same domain behavior instead of copying yesterday’s script into tomorrow’s application.