Decision Tree Validator
Most developers build decision systems.
Few validate them.
This project teaches you how to:
- Model decision trees
- Detect unreachable branches
- Identify logical contradictions
- Validate rule completeness
- Ensure deterministic execution
This is higher-level control flow engineering.
Concept Overview
While watching, focus on:
- Decision tree structure
- Branch coverage
- Logical contradictions
- Default handling
- Deterministic rule evaluation
Step 1 - Understand What We Are Validating
A decision tree consists of:
- Conditions
- Branches
- Outcomes
- Fallback logic
Example:
if age < 18:
category = "minor"
elif age >= 18 and age < 60:
category = "adult"
elif age >= 60:
category = "senior"
else:
category = "unknown"
Is this valid?
We must check:
- Are ranges overlapping?
- Are ranges complete?
- Are branches reachable?
- Is fallback necessary?
Validation means verifying logic correctness before execution.
Step 2 - Represent Decision Tree as Data
Instead of hardcoding conditions, model them.
Example structure:
DECISION_RULES = [
{"condition": lambda ctx: ctx["age"] < 18, "label": "minor"},
{"condition": lambda ctx: 18 <= ctx["age"] < 60, "label": "adult"},
{"condition": lambda ctx: ctx["age"] >= 60, "label": "senior"},
]
This allows dynamic validation.
Step 3 - Basic Decision Engine
def evaluate_decision(context, rules):
for rule in rules:
if rule["condition"](context):
return rule["label"]
return "unknown"
Usage:
result = evaluate_decision({"age": 25}, DECISION_RULES)
print(result)
But this does not validate rule structure.
Step 4 - Detect Unreachable Branches
Example bad tree:
BAD_RULES = [
{"condition": lambda ctx: ctx["age"] > 10, "label": "group1"},
{"condition": lambda ctx: ctx["age"] > 20, "label": "group2"},
]
Second rule is partially unreachable.
Why?
If age > 20, first rule already matches.
We must detect this.
Step 5 - Validation Strategy (Testing Sample Space)
For numeric fields, we can simulate ranges.
Example:
def validate_numeric_tree(rules, test_values):
covered = set()
for value in test_values:
matched = None
for i, rule in enumerate(rules):
if rule["condition"]({"age": value}):
matched = i
break
if matched is not None:
covered.add(matched)
return covered
Test:
test_values = range(0, 100)
coverage = validate_numeric_tree(BAD_RULES, test_values)
print(coverage)
If some rule index never appears → unreachable.
Unreachable branches hide logical bugs.
Step 6 - Detect Missing Default Handling
If evaluate returns None:
def validate_completeness(rules, test_values):
for value in test_values:
result = evaluate_decision({"age": value}, rules)
if result == "unknown":
return False
return True
This detects missing coverage.
Step 7 - Detect Logical Contradictions
Example:
{"condition": lambda ctx: ctx["age"] > 30 and ctx["age"] < 20}
Impossible condition.
We cannot automatically detect arbitrary contradictions, but we can test via sampling.
If condition never evaluates True across valid domain, it is suspicious.
Step 8 - Detect Overlapping Conditions
Overlapping example:
OVERLAP_RULES = [
{"condition": lambda ctx: ctx["age"] > 20, "label": "A"},
{"condition": lambda ctx: ctx["age"] > 30, "label": "B"},
]
If both true for same value, only first executes.
We can detect overlap:
def detect_overlap(rules, test_values):
overlaps = []
for value in test_values:
matches = []
for i, rule in enumerate(rules):
if rule["condition"]({"age": value}):
matches.append(i)
if len(matches) > 1:
overlaps.append((value, matches))
return overlaps
Now overlapping ranges are visible.
Overlapping conditions are not always wrong. But they must be intentional.
Step 9 - Build Full Validator
def validate_tree(rules, test_values):
unreachable = set(range(len(rules)))
overlaps = []
missing = []
for value in test_values:
matches = []
for i, rule in enumerate(rules):
if rule["condition"]({"age": value}):
matches.append(i)
if matches:
unreachable -= set(matches)
if len(matches) > 1:
overlaps.append((value, matches))
else:
missing.append(value)
return {
"unreachable_rules": list(unreachable),
"overlapping_cases": overlaps,
"missing_cases": missing,
}
Usage:
validation_report = validate_tree(DECISION_RULES, range(0, 100))
print(validation_report)
Step 10 - Add Validation Output Formatting
def print_validation_report(report):
print("Validation Report")
if report["unreachable_rules"]:
print("Unreachable rules:", report["unreachable_rules"])
if report["overlapping_cases"]:
print("Overlapping conditions detected")
if report["missing_cases"]:
print("Missing coverage for values:", report["missing_cases"][:10])
Structured reporting improves clarity.
Step 11 - Multi-Dimensional Trees
Example:
RULES = [
{"condition": lambda ctx: ctx["role"] == "admin" and ctx["region"] == "EU", "label": "A"},
{"condition": lambda ctx: ctx["role"] == "admin", "label": "B"},
]
Second rule overlaps with first.
Testing multi-dimensional input space:
roles = ["admin", "user"]
regions = ["EU", "US"]
test_cases = [
{"role": r, "region": reg}
for r in roles
for reg in regions
]
Validator must test combinations.
Step 12 - Prevent Branch Explosion
Instead of many conditions:
Use mapping:
RULE_MAP = {
("admin", "EU"): "A",
("admin", "US"): "B",
}
Then validate keys explicitly.
Mapping-based systems are easier to validate.
Step 13 - Complexity Awareness
Validation cost increases with:
- Number of rules
- Number of dimensions
- Size of test space
Full combinatorial validation may be expensive.
Use sampling intelligently.
Validation systems must balance thoroughness and performance.
Common Mistakes
❌ Assuming rule order is correct
❌ Ignoring unreachable branches
❌ No fallback logic
❌ Overlapping conditions without intention
❌ Deep nested conditions instead of structured modeling
❌ Not testing edge boundaries
Engineering Reflection
Before deploying any rule system, ask:
- Are rules mutually exclusive?
- Is ordering intentional?
- Is default behavior defined?
- Are ranges overlapping?
- Are any conditions impossible?
- Is coverage complete?
If not, redesign.
What You Learned
This project taught you:
- Modeling decisions as data
- Validating execution paths
- Detecting unreachable branches
- Detecting overlaps
- Ensuring completeness
- Designing deterministic control flow systems
You are no longer writing conditionals.
You are validating decision architectures.
That is advanced control flow engineering.
Final Insight
Control flow errors are subtle.
They do not crash systems.
They silently misroute behavior.
A Decision Tree Validator forces correctness before deployment.
In real production systems:
- Pricing engines
- Risk engines
- Access controllers
- Fraud detection systems
All require decision validation.
And now you know how to build one.
