Decision Table
Externalize complex branching logic — pricing tiers, eligibility rules, routing decisions — into a readable table that business users can maintain without a developer in the loop.
On this page
Visual Flow
Rendering diagram…
When to Use This Pattern
Use a decision table whenever:
- The rules change often but the surrounding process doesn't
- Business users understand the rules better than engineers do
- An "if this and this and this, then that" clause is starting to nest past three levels
- The rules need to be auditable — who changed what, and when?
Classic examples: pricing tiers, shipping cost rules, fraud scoring thresholds, approval routing, lead scoring, eligibility checks, plan benefits.
If your if/else tree in code is getting commits from multiple people every week, that's a decision table screaming to be born.
How It Works
A decision table has:
- Input columns: the variables it reads (amount, region, tier, age, …)
- Output columns: what it returns (price, shipping method, approver, …)
- Rows: one rule per row
- Hit policy: what happens when multiple rows match. Common policies:
- Unique — exactly one row should match. Error if more. Good for pricing.
- First — use the first matching row. Good for priority rules.
- Collect — return all matching outputs. Good for eligibility (multiple benefits).
The DMN standard formalises this with a FEEL expression language. You don't need a DMN engine to use the pattern — a versioned CSV and a tiny evaluator will get you 80% of the value.
Picking the wrong hit policy is the most common bug. Start with Unique — it screams the moment you have overlapping rules.
Implementation Guide
Step 1: Identify the inputs and outputs
List every variable that could influence the decision. Be generous up front — it's easier to delete a column than to add one later when you have 200 rows.
Step 2: Draft the rows in a spreadsheet first
Don't start in code. Put the table in a sheet where the business owner can see it and poke at it. Iterate until they agree the rules match reality.
Step 3: Pick a hit policy
For each decision, pick the one that matches your intent. "Unique, fail loudly if multiple match" is a reasonable default.
Step 4: Build a small evaluator
If you're not using a DMN engine, a 50-line evaluator will do. Inputs in, rows checked in order, matching rows' outputs returned per the hit policy. Write thorough tests; this is business-critical logic.
Step 5: Version it
Every change to the table is a diff. Treat the table like code: PRs, review, deploy pipeline. If a non-engineer edits it, the PR should explain why in plain language.
Step 6: Log every evaluation
Record inputs, matched row(s), and output for each call. When a customer asks "why was I charged this?", the log answers. When the rules change, the log lets you replay the old rules against today's data.
Tips & Best Practices
- Keep one concern per table. "Pricing" and "fraud scoring" are separate tables even if they share inputs.
- Cap the table size. If you're over 50 rows, split into sub-tables or move some logic into a proper function.
- Show the table in the app. Users accepting a decision often ask "why?". A read-only view of the matched row answers it.
- Run the table against last month's real data before deploying changes. Regressions in a decision table ship silently and cost money.
Related patterns
Access Review & Recertification
Periodically review who has access to what, confirm it's still needed, and revoke what isn't. Kills the slow-drip accumulation of stale permissions that turn into audit findings.
Immutable Audit Trail
Record every significant action to an append-only log that can be inspected but not altered. Essential for compliance, incident response, and answering 'who changed this and when?'
Human-in-the-Loop Review
Automation handles the routine work, but pauses for a human to review, validate, or make a judgment call at critical decision points. The best of both worlds.