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?'
Compliance Checkpoint Gate
Enforce mandatory review and documentation at specific points in a process to meet regulatory, legal, or internal policy requirements. Non-negotiable gates that cannot be bypassed.