Tout ce que vous devez savoir sur les Business Rules ServiceNow : types, déclencheurs, ordre d’exécution, bonnes pratiques et exemples de code concrets. La référence pour consultants et développeurs.
Les Business Rules sont l’un des outils les plus puissants — et les plus mal utilisés — de la plateforme ServiceNow. Mal écrites, elles sont à l’origine d’innombrables incidents de production : boucles infinies, performances dégradées, comportements imprévisibles à la mise à jour. Bien écrites, elles sont la colonne vertébrale d’une instance maintenable.
Ce guide condense ce qu’un consultant doit absolument savoir en 2025 : quand utiliser une Business Rule, quel type choisir, comment écrire le script, et surtout quelles erreurs évitent un week-end à débugger.
Qu’est-ce qu’une Business Rule ?
Une Business Rule est un script côté serveur, déclenché par un événement de base sur une table donnée : insertion, mise à jour, suppression, ou requête. Contrairement à un Client Script (qui s’exécute dans le navigateur), une Business Rule a accès complet à l’API serveur — donc à GlideRecord, gs.log, aux Script Includes, etc.
L’équivalent dans d’autres mondes : un trigger SQL, mais avec la puissance d’un environnement applicatif complet.
Règle d’or : si vous pouvez le faire avec une Flow, une Data Policy ou une UI Policy, faites-le avec ça. Une Business Rule n’est justifiée que pour la logique qui doit s’exécuter quel que soit le canal d’entrée (UI, API, import, intégration).
Les 4 types : Before, After, Async, Display
Before
Exécutée avant la persistance. C’est ici qu’on modifie current sans coût (pas d’update() nécessaire). Idéal pour : valeurs par défaut, normalisation, calculs dérivés, validation bloquante avec current.setAbortAction(true).
// Before insert/update on incident
// Normaliser la priorité en fonction de l’urgence + impact
(function executeRule(current, previous /*null when async*/) {
if (current.urgency.changes() || current.impact.changes()) {
current.priority = computePriority(current.urgency, current.impact);
}
})(current, previous);
After
Exécutée après la persistance. current a déjà l’ID définitif et est en base. Idéal pour : créer des enregistrements liés, déclencher une notification, journaliser, mettre à jour une autre table.
Ne modifiez pas current dans une After sans appeler current.update() — sinon vos changements sont perdus.
Async
Comme After, mais découplée : exécutée par le scheduler dans une transaction séparée. C’est le bon choix dès que la logique n’a pas besoin d’être visible immédiatement à l’utilisateur (envoi d’email, sync vers un système externe, calcul lourd).
Display
Exécutée au chargement du formulaire. Sert principalement à pousser des données dans g_scratchpad à destination des Client Scripts (typiquement, des résultats de Script Include nécessaires côté UI).
// Display Business Rule
(function executeRule(current, previous) {
g_scratchpad.userIsManager = gs.getUser().hasRole(‘itil_admin’);
g_scratchpad.relatedTickets = new IncidentUtils().countOpenForCaller(current.caller_id);
})(current, previous);
When to run : conditions et déclencheurs
Le champ When détermine quand le script tourne ; le champ Filter Conditions détermine sur quels enregistrements. Quelques règles de bon sens :
- Préférez les Filter Conditions au
ifen début de script : c’est filtré côté DB, donc plus rapide. - N’utilisez Insert + Update que si la logique est strictement identique. Sinon, séparez en deux Business Rules nommées clairement.
- Le combo Async + Update sur une table à fort volume (incident, task) est un grand classique du noisy neighbor : si vous le faites, vérifiez le coût avec
System Diagnostics → Stats.
Ordre d’exécution et priorités
L’ordre des Business Rules sur une même table suit le champ order (ascendant). Les valeurs par défaut sont 100 et 200 ; conservez 0–999 pour vos rules métier et réservez 1000+ pour les rules transverses (audit, sync). Documentez l’ordre dans la description — futur-vous vous remerciera.
| Étape | Type | Conseil |
|---|---|---|
| 1 | Before | Validation, normalisation, valeurs dérivées |
| 2 | Persistance | (automatique) |
| 3 | After | Effets côté DB nécessaires immédiatement |
| 4 | Async | Effets différés, externes, lourds |
Exemples concrets commentés
Exemple 1 — Empêcher la fermeture d’un incident sans description de résolution
Astuce// Type: Before, When: update, Filter: state changes to Resolved
(function executeRule(current, previous) {
if (gs.nil(current.close_notes)) {
gs.addErrorMessage("Vous devez renseigner les notes de résolution avant de fermer.");
current.setAbortAction(true);
}
})(current, previous);
Exemple 2 — Auto-assignation au manager de la CI lors de la création
Astuce// Type: Before, When: insert, Filter: cmdb_ci is not empty AND assigned_to is empty
(function executeRule(current, previous) {
var ci = new GlideRecord('cmdb_ci');
if (ci.get(current.cmdb_ci.toString())) {
if (!gs.nil(ci.managed_by)) {
current.assigned_to = ci.managed_by;
current.assignment_group = ci.support_group;
}
}
})(current, previous);
Exemple 3 — Async : notifier un système externe via Scripted REST
// Type: Async, When: after insert OR update
// Délègue toute la logique à un Script Include — la BR reste mince.
(function executeRule(current, previous) {
new x_corp_sync.IncidentMirror().push(current.getUniqueValue());
})(current, previous);
Pièges classiques à éviter
- Boucle infinie sur
current.update()dans une After. Si vous appelezupdate(), la BR se redéclenche. Solution : guardez aveccurrent.changes()ou utilisez Before. setWorkflow(false)oublié. Quand vous mettez à jour une autre table, désactivez le workflow et les Business Rules cibles si elles ne sont pas pertinentes — sinon vous cascadez sans le vouloir.- Logique métier dans la BR plutôt que dans un Script Include. Une BR doit appeler ; pas implémenter. C’est la seule façon de tester unitairement.
- Pas de
previousdans Async. Le parampreviousestnulldans une Async — utilisezcurrent.changes()avec prudence (elle compare à la valeur en DB au moment de l’exécution, pas au moment du déclenchement). - Trop de BR sur une même table. Au-delà de ~30 BR actives sur une table chaude (incident, task), regroupez ou migrez vers Flow Designer.
Bonnes pratiques de production
- Nommez clairement.
Incident — auto-assign from CI managerbatBR1. - Une responsabilité par BR. Si la description fait plus de deux phrases, divisez.
- Délégez aux Script Includes. Le script de la BR doit tenir en moins de 20 lignes idéalement.
- Tracez avec
gs.info(). Pasgs.log()— on le retrouve mieux dans les System Logs. - Désactivez plutôt que supprimer. Une BR désactivée garde son historique d’audit.
- Update Set discipline. Une BR par Update Set quand c’est possible — ça facilite les rollbacks.
FAQ rapide
Quand utiliser une Flow plutôt qu’une Business Rule ?
Si la logique est linéaire, visualisable et maintenable par un fonctionnel, choisissez Flow Designer. Pour de la logique pure code (calculs, manipulation de données complexes, intégrations), Business Rule reste plus adapté.
Comment tester une Business Rule ?
Trois leviers : gs.info() dans le script + System Logs, Background Scripts pour exécuter le code à la main, et surtout : déléguez la logique à un Script Include et testez celui-ci avec ATF.
Async ou After ?
Si l’utilisateur n’a pas besoin du résultat immédiatement (et que ça ne casse pas un workflow visible), Async. Sinon After.