A new product line launches and routing rules change across seven script includes, three business rules, and a workflow condition. Tracing what changed and when takes a week of code archaeology. Decision Tables exist precisely for this — externalize the rules into a structured artifact a non-developer can read. The trap is using them like a script, which gives you all the pain and none of the benefits.
When a Decision Table beats script
Use a Decision Table when the logic is “given these inputs, choose one of these outputs,” the rules change frequently, and a non-developer should be able to read or update them. Routing tickets to support groups, mapping incident categories to priority, choosing approval chains by region — these are textbook fits. Calculating SLAs based on continuous variables or executing side effects — script.
Inputs are typed and bounded
Every input column has a type (string, choice, reference, integer) and ideally a bounded value list. “Region” should be a choice list of North America | EMEA | APAC | LATAM, not a free-text string. The decision table editor validates rule cells against the type, which prevents the misspelled-region rule that silently never matches.
Inputs:
category (choice: hardware | software | network | facilities)
region (choice: NA | EMEA | APAC | LATAM)
business_critical (boolean)
Output:
assignment_group (reference: sys_user_group)
Order matters less than overlap
A well-designed Decision Table has non-overlapping rules — for any input combination, exactly one rule matches. Rules ordered top-to-bottom with first-match semantics are the default; this works for short tables and breaks down at 50 rows when nobody can hold the order in their head. Use “unique” hit policy where possible; the editor flags overlap at design time.
Don’t build hierarchies in a single table
A 200-row Decision Table is unmaintainable regardless of how clean the inputs are. Decompose: a top-level table that picks a sub-table based on coarse criteria (region, then within each region a table of category-to-group mappings). The top level changes rarely; sub-tables change often and in isolation. Each table stays under 30 rows.
Versioning and audit
Decision Tables are platform records and follow update-set propagation. The editor does not show meaningful diffs across versions out of the box; capture every meaningful change in the table’s description with a one-line entry (date, author, ticket, summary). When routing breaks, the description should be the first place a triager looks.
// Calling a Decision Table from a Business Rule
var dt = new sn_dt.DecisionTableAPI();
var result = dt.evaluate('IncidentRouting', {
category: current.category,
region: current.location.country,
business_critical: current.business_service.business_critical
});
current.assignment_group = result.assignment_group;
Empty match is a feature, not a bug
A Decision Table evaluation that finds no matching rule should fail loudly to a default branch, not silently fall through. Either configure a default rule (catch-all bottom row) or check the result for empty in the calling code and route to a triage queue. Silent no-match means tickets land nowhere and get noticed only when an SLA breaches.
Common failure modes
Free-text fields used as inputs (description, short description) — the table cannot express text matching well; use a categorization step upstream and feed the categorized result. Decision Tables called inside loops without caching — each evaluation has overhead; cache results keyed by input combination for batch processing. Logic that mixes routing with side effects (notifications, field updates) — the table should return outputs only; the caller orchestrates side effects.
Implementation sequence
Pick one routing decision currently buried in script. Extract the rules into a Decision Table, validate against historical tickets (apply the table to last month’s tickets and compare against actual routing), then cut over with feature-flag fallback. After one decision is stable in production for a month, extract the next.
What to do this week: identify the script include with the most “if region == X” branches in your routing logic — that is your first Decision Table candidate.