Every ServiceNow team argues about this, usually without realising they are arguing about it. Someone needs a field set when a record is saved, and they write a business rule — because that is what the last person did. Three years later the instance has four hundred business rules, nobody can explain why a field changes on save, and every enhancement takes a week because the first four days are archaeology.
The platform gives you five or six places to put logic. They are genuinely different, and the choice is not stylistic — it determines whether the next person can debug what you built.
What each mechanism is actually for
Business rules react to database operations. They are the right tool when something must happen no matter how the record was touched — the UI, an import, a REST call, a script running at 3am.
The four types are not interchangeable:
- Before — modify the record currently being written. No extra database write. This is where field defaulting and derivation belongs.
- After — act on other records once this one is committed.
- Async — expensive work moved off the user’s transaction. Use it more than you do.
- Display — prepare data for the form, typically via
g_scratchpad.
The most common efficiency mistake is doing in an after rule what belongs in a before rule, which turns one write into two.
Script includes are where reusable server-side logic belongs. Classes, on-demand functions, client-callable AJAX. The discipline that separates maintainable instances from the rest: a business rule should be a thin trigger that calls a script include. Five lines of condition and delegation, with the actual logic somewhere it can be reused, tested, and found by search.
Flow Designer is for orchestration — processes with steps, waits, approvals, human tasks, and integrations. Its decisive advantage is not that it is declarative. It is the execution history: a visual, per-run trace showing exactly which steps ran, with what data, and where it stopped. Debugging a flow means reading that trace. Debugging an equivalent cascade of business rules means reading logs and guessing.
UI policies handle show/hide, mandatory, and read-only on the form. Always reach for these before a client script — they are declarative, they run in a defined order, and they reverse cleanly.
Data policies are the server-side equivalent of a UI policy, enforcing field requirements regardless of entry path. Underused, and often the right answer where people write a validating business rule.
The framework
Four questions, in order:
1. Must this hold no matter how the record is changed? Business rule or data policy. A flow can be bypassed by an import set or a direct API write; a database-level rule cannot. Anything protecting data integrity goes here.
2. Is this a multi-step process a non-developer should be able to read? Flow Designer. Approvals, notifications with waits, provisioning sequences, anything with a human in the middle. Process owners can read a flow. They cannot read your script include, and in three years neither will you.
3. Is this reusable computation? Script include, called from wherever needs it. If two business rules contain similar code, the logic belonged in a script include before the second one was written.
4. Is this purely about form behaviour? UI policy first. Client script only when the behaviour is genuinely beyond what a policy expresses.
Most disagreements dissolve once people notice they are answering different questions. “Should this be a flow or a business rule” is usually two requirements wearing one ticket: an integrity rule and a process, which want different homes.
The cascade that ruins instances
Here is the failure mode that makes long-lived instances unmaintainable.
A business rule on Table A updates a record on Table B. A rule on Table B updates related records on Table C. A rule on Table C writes back to Table A. Each rule is individually sensible, written by a different person, in a different year, for a different requirement.
Now a user changes one field and seventeen records update. Something is wrong somewhere. There is no single place to look, because there is no single place where the behaviour is expressed — it is emergent, distributed across three tables and a decade.
This is the strongest practical argument for Flow Designer on anything process-shaped. A flow has one place where the process is written down and one trace showing what it did. A cascade has neither.
Where you must have cross-table rules, two habits contain the damage: keep rules narrow and specific rather than broad and clever, and write down the intent in the rule description — the next person’s only clue about why this exists.
Anti-patterns worth naming
Global business rules with no condition. A rule with an empty condition runs on every operation on that table forever. On a busy table that is a permanent performance tax for logic that applies to two percent of records. Condition first, always.
Logic in the business rule instead of a script include. Untestable, unreusable, and invisible to anyone searching for the behaviour. It also guarantees the copy-paste duplicate when the same need arises elsewhere.
current.update() inside a before rule. The record is about to be written anyway. Calling update explicitly causes a second write and can trigger the rule again. This is a top source of mysterious recursion.
Order 100 on everything. The order field exists to sequence rules, and when every rule shares a value the sequence is arbitrary. The day two rules on one table start conflicting, you will need this and it will not be there.
Synchronous work that should be async. Integrations, bulk updates, and anything calling out to another system inside a synchronous rule makes users wait on someone else’s uptime.
Deeply nested subflows. Flow Designer’s readability advantage disappears once tracing a process means opening six subflows. Two levels is comfortable; four is a script with extra steps.
Testing and the long view
Script includes are the only one of these that is genuinely unit-testable, which is another argument for putting the logic there. Flows are testable through Automated Test Framework, and ATF coverage of your core flows is one of the few things that makes upgrades boring instead of frightening.
That upgrade dimension is worth weighing when the decision is close, and it is fundamentally an architecture question rather than a coding-style one. Declarative configuration survives platform upgrades better than script, and — more importantly — it survives staff turnover better. The next team can read a flow. Whether they can read four hundred business rules is the question that decides how much your instance costs to own.
This is the same reasoning that governs how much custom UI to build versus using what the platform provides: the platform’s own constructs are the ones the next person will understand, and stepping outside them should be a deliberate purchase rather than a habit.
If you are inheriting a mess
Do not rewrite. Do this instead:
- Inventory the business rules on your busiest tables. Sort by table, look for rules with no condition and rules whose descriptions are empty.
- Find the cascades. For each after-rule that writes to another table, check what rules exist on that table. Draw it. The picture is usually worse than anyone expects and immediately explains several long-standing mysteries.
- Extract logic into script includes as you touch each rule, rather than in a big-bang refactor.
- Move new process work into flows, and let the old business rules age out where they are.
That sequence gets an instance back to comprehensible over a few quarters without a risky rewrite. It is a large part of what our ServiceNow development and architecture work looks like in mature instances — less new development, more making the existing behaviour explainable again, which is the precondition for everything else.
If your instance has reached the stage where nobody can say why a field changes on save, that is a solvable problem, and it does not require starting over.