# Decision Tables and Trees

### Quick Summary

* Use a Decision Table DT when there are multiple rules, and different combinations of them lead to different results.
  * Example: *If condition 1 AND AND condition 2 OR condition 3 THEN do X* — when logic gets messy, a decision table helps organize such logic.
* **DT core limitation**: it grows exponentially.&#x20;
  * 5 conditions = 2⁵ = 32 combinations; 10 conditions = 2<sup>10</sup> = 1024.&#x20;
  * 5 Conditions are an approximate limit, after which the DT becomes difficult to understand, change, or manage.
  * It's possible to simplify a DT.
  * It is sometimes appropriate to break up a DT into several smaller ones (one table per equivalence partition or state).
  * Possible DT alternatives:
    * Rule Engines (e.g. Drools): for systems with many (many!) dynamic, complex rules.
    * Pairwise Testing technique: unlike a DT, does not exhaustively define all rule combinations, but effectively reduces test cases while ensuring key combinations are tested.

### Template

<table><thead><tr><th width="300.81817626953125">Conditions</th><th align="center">Rule (Test) 1</th><th align="center">Rule  2</th></tr></thead><tbody><tr><td>Condition 1</td><td align="center">Y</td><td align="center">N</td></tr><tr><td>(Optional blank row for visual clarity)</td><td align="center"></td><td align="center"></td></tr><tr><td>Action  / Result  / Output</td><td align="center">X</td><td align="center"></td></tr></tbody></table>

### Simple example

> Applicant must be over 18 to qualify for {thing}

| Condition           | Rule 1 | Rule 2 |
| ------------------- | :----: | :----: |
| Age ≥ 18            |    Y   |    N   |
| Accept Application? |    X   |        |

* `Y / N` stands for Yes / No. It can also be `T / F` (True / False)
* `X` is used for Actions and means "Yes". `Blank` means "no" or "no action". You could use Y/N for action rows too, but it becomes harder to read the table. Using X helps separate **"what is"** (conditions) from **"what to do"** (actions).
* `-` or `N/A` means "not applicable"
* There can be **many Conditions and many Actions**
* The table grows at 2<sup>n</sup> rate.&#x20;
  * 1 condition: 2<sup>1</sup> = 2 columns (rules)
  * 2 conditions: 2<sup>2</sup> = 4 columns
  * 3 conditions 2<sup>3</sup> = 8 columns
  * etc.

### Intermediate example

> * Applicant must be over 18 to qualify for {thing}
> * Fast-track review with residency card, standard review without it

<table><thead><tr><th width="157.1817626953125">Condition</th><th align="center">Rule 1</th><th align="center">Rule 2</th><th align="center">Rule 3</th><th align="center" valign="top">Rule 4</th></tr></thead><tbody><tr><td>Age ≥ 18</td><td align="center">Y</td><td align="center">Y</td><td align="center">N</td><td align="center" valign="top">N</td></tr><tr><td>Has res. card</td><td align="center">Y</td><td align="center">N</td><td align="center">Y</td><td align="center" valign="top">N</td></tr><tr><td></td><td align="center"></td><td align="center"></td><td align="center"></td><td align="center" valign="top"></td></tr><tr><td>Fast-track review</td><td align="center">X</td><td align="center"></td><td align="center"></td><td align="center" valign="top"></td></tr><tr><td>Standard Review</td><td align="center"></td><td align="center">X</td><td align="center"></td><td align="center" valign="top"></td></tr><tr><td>Reject</td><td align="center"></td><td align="center"></td><td align="center">X</td><td align="center" valign="top">X</td></tr></tbody></table>

You'll notice that adding a second condition (has res. card) doubled the number of rules: 2<sup>2</sup>=4.

### Filling in Condition Rows

Looking at the condition rows and all those `YY-NN-Y-N-Y-N`, one may wonder how to fill them in without getting lost.

Assuming Y/N (boolean) are the only possible values, like so:

> Break up the first row in half, fill the 1st half with Y, 2nd with N:
>
> Condition 1: Y  Y  Y  Y  N  N  N  N\
> \
> Take half of the condition above, and fill the next row with half Y, half N:\
> \
> Condition 1: Y  Y  Y  Y  N  N  N  N\
> Condition 2: Y Y  N N \
> Condition 3  Y N\
> \
> Now you have the "repeating pattern" for each row and you can fill them in:\
> \
> Condition 1: Y  Y  Y  Y  N  N  N  N\
> Condition 2: Y Y  N N  Y  Y   N  N\
> Condition 3  Y N  Y N  Y N   Y   N

### Simplifying The Table&#x20;

#### One Action Row to Rule Them All

When actions are mutually exclusive (you can't "approve" and "reject" at the same time), consider using just one row.

> * One "Action" row
> * Insert action values instead of 'X'

<table><thead><tr><th width="157.1817626953125">Condition</th><th align="center">Rule 1</th><th align="center">Rule 2</th><th align="center">Rule 3</th><th align="center" valign="middle">Rule 4</th></tr></thead><tbody><tr><td>Age ≥ 18</td><td align="center">Y</td><td align="center">Y</td><td align="center">N</td><td align="center" valign="middle">N</td></tr><tr><td>Has res. card</td><td align="center">Y</td><td align="center">N</td><td align="center">Y</td><td align="center" valign="middle">N</td></tr><tr><td></td><td align="center"></td><td align="center"></td><td align="center"></td><td align="center" valign="middle"></td></tr><tr><td>Action</td><td align="center">Fast-track Review</td><td align="center">Standard Review</td><td align="center">Reject</td><td align="center" valign="middle">Reject</td></tr></tbody></table>

{% hint style="info" %}
When more than one action applies in a rule (e.g., "Send email" AND "Log event"), then it makes sense to have one action per row and mark with X.
{% endhint %}

#### Merging Rules

It's possible to combine (collapse) rules where it is apparent that an alternative does not make a difference in the outcome.

> This:\
> \
> Condition 1  :  N  N  \
> Condition 2 :   Y  N
>
> Action 1       :   X  X\
> \
> Can be expressed as:\
> \
> Condition 1  : N    \
> Condition 2 :  -                (dash \[-] means "either Y or N, doesn't matter")\
> Action 1       :  X

An example of a table with merged rules:

> Given that underage applicants will always be rejected (poor beings), we can merge Rules 3 and 4 (and all future Rules with `Age ≥ 18 = N`).

| Condition     |       Rule 1      |      Rule 2     | Rule 3 |
| ------------- | :---------------: | :-------------: | :----: |
| Age ≥ 18      |         Y         |        Y        |    N   |
| Has res. card |         Y         |        N        |    -   |
|               |                   |                 |        |
| Action        | Fast-track Review | Standard Review | Reject |

The above example reduces the number of rules by 1 (from 4 to 3). This may seem insignificant, but the savings grow with each added condition:

* 3 conditions → 2³ = 8 rules, half of which would have `Age ≥ 18 = N` , thus we **merge 4 rules into 1**
* 4 conditions → 2<sup>4</sup> = 16 rules, we can **merge 8 rules into 1**

{% hint style="info" %}
The above example shows only ONE row that offers an opportunity to merge rules.

ANY ROW may allow merging, not just the 1st one.

A table may have MULTIPLE rows that allow rule merging.
{% endhint %}

#### Merging Conditions

Watch out for logically inverse conditions. They are redundant.

> Either of the conditions can be removed and the table will not lose meaning or coverage

| Condition                                              |  Rule 1 | Rule 2 |
| ------------------------------------------------------ | :-----: | :----: |
| Income > 60k                                           |    Y    |    N   |
| <p>Income < 60k </p><p>(^ inverse, can be removed)</p> |    N    |    Y   |
|                                                        |         |        |
| Action                                                 | Approve | Reject |

### Checking for Completeness and Accuracy

The following problems can occur in decision tables:&#x20;

1. Condition and Rule redundancy (demonstrated above by merging rules)
2. Incompleteness
3. Impossible situations or contradictions

#### How do I check for incompleteness?

* If there are no merged rules, and assuming only `Y/N` values, your table **must have exactly 2**<sup>**n**</sup>**&#x20;rules**. Fewer indicates missing rules.
* If there are merged rules, **then it depends**. Assuming a simple scenario from above where the same value always leads to the same result ( `Age ≥ 18 = N` → `Reject` ), you can calculate the correct number of rules that you should have:

<table><thead><tr><th width="162.45452880859375">Total Conditions</th><th width="135.9090576171875">Total Rules</th><th>Mergeable (exactly half)</th><th>Reduced Rules After Merge</th></tr></thead><tbody><tr><td>3</td><td>8</td><td>4 → 1</td><td>5</td></tr><tr><td>4</td><td>16</td><td>8 → 1</td><td>9</td></tr><tr><td>5</td><td>32</td><td>16 → 1</td><td>17</td></tr><tr><td>n</td><td>2ⁿ</td><td>2ⁿ⁻¹ → 1</td><td>2ⁿ⁻¹ + 1</td></tr></tbody></table>

If your table with 5 conditions has 15 rules, it may be missing 2 rules (should be 17).

{% hint style="info" %}
In other cases, you will have DIFFERENT logic and a DIFFERENT way of deducing whether your table is incomplete.
{% endhint %}

#### How do I find impossible situations or other issues?

It is arguably impossible to list an exhaustive list of problems in decision tables. One must rely on strong logical reasoning. Nonetheless, here are a few examples.

> **Example 1**:&#x20;
>
> * **Rule 1: overlapping truth**. If someone earns **≥ 60k**, it is *also* true that they earn **≥ 30k** - so both conditions being `Y` is **logically valid**, but potentially **redundant or overlapping**.
> * **Possible fix**: redefine 2nd condition to `30k ≤ Income < 60k`

| Condition    | Rule 1 ❌ |  Rule 2 | Rule 3 | Rule 4 | Rule n |
| ------------ | :------: | :-----: | :----: | :----: | ------ |
| Income ≥ 60k |     Y    |    Y    |    N   |    N   |        |
| Income ≥ 30k |     Y    |    N    |    Y   |    N   |        |
|              |          |         |        |        |        |
| Action       |  Approve | Approve | Review | Reject |        |

> **Example 2: Direct contradiction**
>
> * If we KNOW that a passport was ISSUED to a person, how can we then assert that the person has NO valid passport. What does "valid" mean anyway?
> * This example also highlights that contradicting values m**ay be many rows apart and thus not obvious**.
> * **Possible fix**: clarify condition definitions. Perhaps `Passport Expired` instead of `Valid` (which is too broad)?

<table><thead><tr><th width="173.3636474609375">Condition</th><th align="center">Rule 1</th><th align="center">Rule 2</th><th align="center">Rule 3 ❌</th><th>Rule n</th></tr></thead><tbody><tr><td>Has Valid Passport</td><td align="center">Y</td><td align="center">N</td><td align="center">N</td><td></td></tr><tr><td>Is Resident of Country A</td><td align="center">Y</td><td align="center">Y</td><td align="center">N</td><td></td></tr><tr><td><p>Passport Issued </p><p>by Country A</p></td><td align="center">Y</td><td align="center">N</td><td align="center">Y</td><td></td></tr><tr><td></td><td align="center"></td><td align="center"></td><td align="center"></td><td></td></tr><tr><td>Action</td><td align="center">Auto Verify</td><td align="center">Manual Check</td><td align="center">???</td><td></td></tr></tbody></table>

### Pivoting the Table

**It is possible to have rules as both columns and rows.**&#x20;

There is little difference when the tables are small, but starting from ca. 2<sup>3</sup>=8 and more rules, it may become inconvenient to have rules as columns (seen until now). 8+ rules as columns might look OK in a spreadsheet or a specialised tool on a wide screen, but much less so on static websites or wikis such as this one.&#x20;

In such cases, consider pivoting the table (flipping rows and columns).

> 🚗 Example: Car Insurance Premium Decision Table
>
> 3 binary conditions:
>
> * Gender: Male / Female
>
> * Age: Under 21 / 21 or Older
>
> * Car Type: Normal / Sports Car
>
> We assume these affect price. For example:
>
> * Sports cars cost more
> * Young drivers cost more
> * Young males driving sports cars pay the most (we wonder why)

| Condition       | Rule 1 | Rule 2 | Rule 3 | Rule 4 | Rule 5 | Rule 6 | Rule 7 | Rule 8 |
| --------------- | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ |
| Gender          | M      | M      | M      | M      | F      | F      | F      | F      |
| Age ≥ 21        | Y      | Y      | N      | N      | Y      | Y      | N      | N      |
| Sports Car      | N      | Y      | N      | Y      | N      | Y      | N      | Y      |
| **Premium ($)** | 1200   | 1600   | 1800   | 2500   | 1000   | 1400   | 1500   | 2200   |

{% hint style="info" %}
Notice 2 new things about the above table:

* The first row is in the format `Gender=M/F`, as opposed to `Is Male=Y/N`. This is OK because there are still only two options, and it reads fluently.
* There is `Premium={value}` instead of `Specific Action=X` or `Action=Verb`. This is OK too. Whatever helps common understanding.
  {% endhint %}

Chances are, whatever device you're using right now, the table view is truncated, and you must scroll back and forth.&#x20;

Here is the same table but pivoted.

| Rule # | Gender | Age ≥ 21 | Sports Car | Premium ($) |
| ------ | ------ | -------- | ---------- | ----------- |
| 1      | M      | Y        | N          | 1200        |
| 2      | M      | Y        | Y          | 1600        |
| 3      | M      | N        | N          | 1800        |
| 4      | M      | N        | Y          | 2500        |
| 5      | F      | Y        | N          | 1000        |
| 6      | F      | Y        | Y          | 1400        |
| 7      | F      | N        | N          | 1500        |
| 8      | F      | N        | Y          | 2200        |

This format is preferred by many and follows the typical format of "1 test case per row".

Speaking of test cases, we can use this pivoted format to fill the table with concrete test border values.

### Filling With Test Data

> 🚗 Car Insurance Premium – With Example Values
>
> Notice that:
>
> * The "age" column was moved to the left. As the author of a test model, you can decide the order to achieve greatest possible clarity.
> * If we replace "Rule" with "Test Case", we'll get a classic test case table.
> * Ages 20 and 21 are border values. You may add 22 as `min+` for additional testing.

| **Rule** | **Age** | **Gender** | **Car Type** | **Premium ($)** |
| -------- | ------- | ---------- | ------------ | --------------- |
| Rule 1   | 20      | Male       | Sports       | 2500            |
| Rule 2   | 20      | Male       | Normal       | 1800            |
| Rule 3   | 20      | Female     | Sports       | 1500            |
| Rule 4   | 20      | Female     | Normal       | 2200            |
| Rule 5   | 21      | Male       | Sports       | 1600            |
| Rule 6   | 21      | Male       | Normal       | 1200            |
| Rule 7   | 21      | Female     | Sports       | 1400            |
| Rule 8   | 21      | Female     | Normal       | 1000            |

### Truth vs. Extended Decision Tables

Up until now, all Decision Tables have been **Truth Tables** - they only allow conditions with binary values (`Yes/No`, `Male/Female`, `Under/Over 21`, `Normal/Sports`). They grow at 2<sup>n</sup>.&#x20;

The next logical question is "*but what if we have business rules that demand non-binary values*"? In other words, what if we have many partitions?

Examples:

* 3 age groups: `x < 21`, `21 ≤ x < 65`, and `x ≥ 65`
* 3 (or more) car types: `Family`, `Sports`, `All Other`

It's entirely normal to allow 3+ values in a condition. Such tables are called **Extended Decision Tables**.

Then they no longer grow at 2<sup>n</sup>. Instead:

#### 🔢 **Formula for Total Rules**:

If you have:

* `C₁` possible values for Condition 1
* `C₂` possible values for Condition 2
* `C₃` possible values for Condition 3
* ...

Then total rules = `C₁ × C₂ × C₃ × ... × Cₙ`

Given the example of Car Insurance:

* Age: Under 21 / 21–64 / 65+ → **3 values**
* Gender: Male / Female → **2 values**
* Car Type: Family / Sports / Other → **3 values**

Total rules = 3 × 2 × 3 = **18 rules (before merging)**

### Changing to Decision Trees

You can represent a (simpler and smaller) Decision Table with a Decision Tree.

Let's take a Table with 3 binary conditions (gender, age, car type) that has 8 rules and recreate it as a Tree.

<img src="/files/qQ9dimNKGzirM8D4jNFl" alt="" class="gitbook-drawing">

**✅ Use a Decision Tree when:**

* You want a **visual, easy-to-follow flow** of decisions.
* You need to **explain logic to non-technical users**.
* Some conditions matter more early.
* You want to **quickly trace a path** from input to outcome.

**❌ Avoid Decision Trees when:**

* You have **many conditions with complex combinations**.

**Advantages over Decision Tables:**

* Easier to understand at a glance.
* Naturally represents **prioritization** of conditions.

**Downsides:**

* It can get large and messy with many branches. (Drawing and re-drawing take time).

### Open-source and Free Tools

1. Decision Table Online: <https://rendersnail.com/tools/decision-table-online>
2. Drools: <https://www.drools.org/>

### Further Reading

1. <https://www.hillelwayne.com/post/decision-table-patterns/>
2. Factoring large and complex decision tables into several smaller ones

{% file src="/files/uUq1emXUB6K9HdkfwatX" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://practical-testing.gitbook.io/home/techniques/decision-tables-and-trees.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
