Building Decision Tables for Complex Business Rules

Building Decision Tables for Complex Business Rules

Most examples of decision tables use two or three conditions. Real business rules rarely do. Insurance eligibility, loan approval, tax calculation, shipping logic — these systems have five, six, seven conditions, each interacting with the others. The naive approach — enumerate every combination — produces tables with 128 or 256 columns that nobody can review, maintain, or derive useful tests from.

This post covers the techniques for making decision tables tractable at real-world scale: collapsing redundant rules, handling don't-care conditions properly, splitting tables by concern, and working through a complete shipping rules example.

The Scale Problem

Start with the math. For N binary conditions:

Conditions Max rules
2 4
3 8
4 16
5 32
6 64
7 128
8 256

At 5 conditions, 32 rules is already borderline — it fits on one page but barely. At 7 conditions, 128 rules is completely unmanageable. The techniques below bring these numbers to something workable.

Technique 1: Identify Dominant Conditions

Not all conditions are equal. Some conditions, when they take a particular value, determine the outcome regardless of everything else.

Example: in a loan application, if the applicant has an active bankruptcy, the answer is always "decline" — full stop. No need to enumerate all combinations of credit score, income, and debt ratio when bankruptcy is Y.

Identify these "knockout" conditions first. For each one, add a single collapsed rule with that condition as Y and everything else as —. Then enumerate the remaining rules only for the case where the knockout condition is N.

This alone can cut your table size dramatically.

Technique 2: Collapse Redundant Rules

Two rules are candidates for collapsing when:

  1. They differ in exactly one condition
  2. All their action entries are identical

When both conditions hold, replace the two rules with one, marking the differing condition as — (don't care).

Example:

R1 R2
Credit score >= 700 Y Y
Income >= $50k Y N
Bankruptcy flag N N
Result: Approve X X

R1 and R2 have identical actions and differ only in "Income >= $50k". Collapse to:

R (collapsed)
Credit score >= 700 Y
Income >= $50k
Bankruptcy flag N
Result: Approve X

Repeat this process iteratively. After one round of collapsing, you may find new candidates that weren't adjacent before.

Caution: Only collapse rules when both conditions hold simultaneously. Don't collapse rules just because they look similar — verify the actions match exactly. And be aware that collapsing can obscure boundary conditions, so use BVA alongside the table for numeric thresholds.

Technique 3: Split Tables by Decision Point

Large tables are often trying to do too much. If your system makes a sequence of decisions, split it into separate tables — one per decision point.

Example: a loan approval process has two stages:

  1. Pre-qualification (income, credit score, bankruptcy)
  2. Rate determination (for approved applicants: DTI ratio, loan term, property type)

Don't build one giant table covering all stages. Build two smaller tables and chain them: the action of table 1 ("qualified" / "declined") becomes a condition in table 2.

This keeps each table under 4-5 conditions and makes the overall logic far more readable. It also makes it easier to see which tests cover which stage of processing.

Technique 4: Use Extended Entry for Multi-Value Conditions

When a condition has more than two meaningful states, limited entry tables (Y/N) force you to split it into multiple binary conditions. Extended entry tables handle this directly.

Instead of:

  • C1: Age >= 18 (Y/N)
  • C2: Age >= 65 (Y/N)

Use:

  • C1: Age tier (Under 18 / 18-64 / 65+)

This single extended-entry condition replaces two binary conditions, halving that dimension of the table. The tradeoff is slightly more complex table structure — you can no longer mechanically check completeness by counting 2^N rules.

Worked Example: E-Commerce Shipping Rules

A realistic shipping rules engine with these conditions:

  1. Member tier: Gold, Silver, None
  2. Order total: Under $50 / $50–$99 / $100+
  3. Shipping speed selected: Standard / Express / Overnight
  4. Product type: Regular / Oversized / Hazmat

The possible actions:

  • Free shipping
  • Standard rate ($5.99)
  • Express rate ($12.99)
  • Overnight rate ($24.99)
  • Oversized surcharge (+$15)
  • Hazmat surcharge (+$25)
  • Express/Overnight not available (hazmat restriction)

Step 1: Identify Knockouts

Hazmat products can only ship Standard — Express and Overnight are not available regardless of member tier or order total. Add this as a collapse rule first:

Knockout Rule
Product type Hazmat
Member tier
Order total
Speed selected Express OR Overnight
Action Show "Not available" error

And:

Hazmat Standard
Product type Hazmat
Member tier
Order total
Speed selected Standard
Action Standard rate + $25 hazmat surcharge

Two rules cover the entire hazmat case. Remove hazmat from the remaining table.

Step 2: Handle Oversized Products

Similarly, oversized products always add a $15 surcharge on top of whatever shipping rate applies. Rather than duplicating every rule for oversized vs regular, represent this as a modifier:

Note in table: all rules apply +$15 oversized surcharge when product type = Oversized.

This is a table annotation, not a separate column. It keeps the core logic table clean.

Step 3: Core Shipping Rate Table (Regular Products)

Now with product type simplified away, three conditions remain: member tier (3 values), order total (3 values), speed (3 values) = 27 combinations maximum.

Free Standard ($5.99) Express ($12.99) Overnight ($24.99)
Gold + Any total + Standard X
Gold + Any total + Express X
Gold + Any total + Overnight X
Silver + $100+ + Standard X
Silver + $50–99 + Standard X
Silver + Under $50 + Standard X
Silver + Any total + Express X
Silver + Any total + Overnight X
None + $100+ + Standard X
None + $50–99 + Standard X
None + Under $50 + Standard X
None + Any total + Express X
None + Any total + Overnight X

This table has 13 rules instead of 27. The collapses:

  • Gold tier gets free standard shipping regardless of order total (3 rules → 1)
  • Silver and None tiers: Express and Overnight rates are the same regardless of order total (6 rules → 2 each)

Step 4: Review for Gaps

Read through every rule. Ask: is there a realistic state I haven't covered?

For this table, we haven't specified what happens if someone selects Express with a $100+ order as a None-tier customer. Looking at the table: "None + Any total + Express" → $12.99. That's covered by the collapsed rule.

What about Gold + $100+ vs Gold + under $50 for Standard shipping? The collapsed "Gold + Any total + Standard" → free covers both.

Step 5: Derive Test Cases

From 13 rules, derive 13 test cases minimum. For collapsed rules (those with —), pick representative values:

  • "Gold + Any total + Standard": test with total = $30 (under $50) — tests the most interesting case where a non-Gold user would pay
  • "Gold + Any total + Standard": add a second test with total = $150 to cover boundary if needed

Add the hazmat and oversized tests from steps 1 and 2. Total: ~18 test cases for a complete shipping rules test suite.

Dealing with "Don't Care" Conditions in Tests

When a rule has a — condition, you have flexibility — but use it deliberately:

  • Pick the most interesting value: the one that would break a naive implementation. If a competitor's logic would charge for shipping, pick the value where your system should give it free.
  • Pick boundary values: if the condition is numeric and the don't-care is because it doesn't matter, test at the boundary to confirm it really doesn't matter.
  • Don't always pick the same default value: rotating through different don't-care values across rules provides incidental coverage of more combinations.

Reviewing the Table for Quality

Before deriving test cases, review the completed table with these checks:

Completeness check: Are there any combinations not covered by any rule? For fully enumerated tables, count the rules (after expanding collapsed ones) and verify it totals to 2^N.

Contradiction check: Are there any two rules with identical condition entries that have different actions? That's a specification error.

Redundancy check: Are there two rules with identical conditions AND identical actions? One is redundant — merge or remove.

Feasibility check: Are there rules with condition combinations that can never occur? Remove them or add an "impossible" annotation rather than testing them.

When Tables Get Too Big: Know Your Limit

If after applying all these techniques you still have more than 20-25 rules, consider whether the table is trying to capture a single cohesive business rule or multiple separate rules bundled together. Almost always it's the latter. Split the table.

A decision table with 8 rules that you can fit on a whiteboard and review with a business analyst in 10 minutes is worth ten times more than a 64-rule table that nobody reads. Comprehensibility is a quality criterion for test design artifacts, not just for code.

The goal is a table that a developer, QA engineer, and product manager can all look at together and agree on — before a single test is written or a single line of code is committed.

Read more

Start now free