business-rule-patterns
SKILL.md
Business Rule Best Practices for ServiceNow
Business Rules are server-side scripts that execute when records are displayed, inserted, updated, or deleted.
When to Use Each Type
| Type | Timing | Use Case | Performance Impact |
|---|---|---|---|
| Before | Before database write | Validate, modify current record | Low |
| After | After database write | Create related records, notifications | Medium |
| Async | Background (after commit) | Heavy processing, integrations | None (background) |
| Display | When form loads | Modify form display, set defaults | Low |
Available Objects
// In Business Rules, these are always available:
current // The record being operated on
previous // The record BEFORE changes (update/delete only)
gs // GlideSystem utilities
Before Business Rules
Use for validation and field manipulation:
// Prevent update if condition not met
;(function executeRule(current, previous) {
if (current.state == 7 && previous.state != 6) {
current.setAbortAction(true)
gs.addErrorMessage("Must resolve before closing")
}
})(current, previous)
// Auto-populate fields
;(function executeRule(current, previous) {
if (current.isNewRecord()) {
current.setValue("caller_id", gs.getUserID())
current.setValue("opened_by", gs.getUserID())
}
})(current, previous)
Never do in Before rules:
- Call
current.update()(causes recursion!) - Query other tables (keep it fast)
- External API calls
After Business Rules
Use for related record operations:
// Create child record when priority is P1
;(function executeRule(current, previous) {
if (current.priority.changesTo(1)) {
var task = new GlideRecord("task")
task.initialize()
task.setValue("short_description", "P1 Follow-up: " + current.number)
task.setValue("parent", current.sys_id)
task.insert()
}
})(current, previous)
// Update parent record
;(function executeRule(current, previous) {
var parent = new GlideRecord("problem")
if (parent.get(current.problem_id)) {
parent.setValue("related_incidents", parent.related_incidents + 1)
parent.update()
}
})(current, previous)
Async Business Rules
Use for heavy processing that shouldn't block the transaction:
// External integration
;(function executeRule(current, previous) {
var integrator = new ExternalSystemIntegration()
integrator.syncIncident(current.sys_id)
})(current, previous)
// Send custom notification
;(function executeRule(current, previous) {
gs.eventQueue("incident.priority.high", current, current.assigned_to, gs.getUserID())
})(current, previous)
Useful Methods
current Methods
current.isNewRecord() // True if insert
current.isValidRecord() // True if record exists
current.getValue("field") // Get field value
current.setValue("field", val) // Set field value
current.setAbortAction(true) // Cancel the operation
current.operation() // 'insert', 'update', 'delete'
current.isActionAborted() // Check if aborted
Field Change Detection
current.priority.changes() // Field changed (any value)
current.priority.changesTo(1) // Changed TO this value
current.priority.changesFrom(3) // Changed FROM this value
current.priority.nil() // Field is empty
previous Comparisons
// Check if field was modified
if (current.state != previous.state) {
gs.info("State changed from " + previous.state + " to " + current.state)
}
// Check specific change
if (current.assigned_to.changes() && !previous.assigned_to.nil()) {
gs.info("Reassignment occurred")
}
Condition Examples
Use conditions to limit when the rule runs:
| Condition | Meaning |
|---|---|
current.active == true |
Only active records |
current.isNewRecord() |
Only on insert |
current.priority.changes() |
Only when priority changes |
gs.hasRole('admin') |
Only for admins |
current.assignment_group.nil() |
Only when unassigned |
Performance Best Practices
- Use conditions - Limit when the rule runs
- Keep Before rules fast - No queries if possible
- Use Async for integrations - Don't block transactions
- Avoid Display rules - Slows form load
- Set Order - Lower numbers run first (100-500 range)
- Check "when to run" - insert, update, delete, query
Common Patterns
Auto-Assignment
// Before Insert/Update
if (current.assignment_group.changes() && !current.assignment_group.nil()) {
var members = new GroupMembers(current.assignment_group)
current.assigned_to = members.getNextAvailable()
}
Cascade Updates
// After Update
if (current.state.changesTo(7)) {
// Closed
var tasks = new GlideRecord("task")
tasks.addQuery("parent", current.sys_id)
tasks.addQuery("state", "!=", 7)
tasks.query()
while (tasks.next()) {
tasks.setValue("state", 7)
tasks.update()
}
}
Weekly Installs
53
Repository
groeimetai/snow-flowGitHub Stars
53
First Seen
Jan 22, 2026
Security Audits
Installed on
claude-code49
gemini-cli48
opencode48
github-copilot48
codex48
cursor47