Deterministic Rules
DTRules is designed to be deterministic: given the same inputs, the rules will always produce the same outputs. This property is critical for compliance, auditing, and reliable business operations.
What is Determinism?
A deterministic system has these properties:
- Repeatability - Same inputs always produce same outputs
- Predictability - Behavior can be analyzed and verified
- Auditability - Decisions can be traced and explained
- Testability - Rules can be thoroughly tested
Why Determinism Matters
Regulatory Compliance
Many business rules implement regulations that require consistent, explainable decisions. Welfare eligibility, insurance coverage, and loan approval must be applied uniformly.
Auditing
When decisions are questioned, you need to reproduce exactly how a determination was made. Deterministic rules enable complete decision replay.
Testing
Unit tests and regression tests rely on predictable outputs. Non-deterministic behavior makes testing unreliable.
Debugging
When something goes wrong, deterministic rules let you reproduce the exact scenario and trace through the logic step by step.
How DTRules Ensures Determinism
No Random Operations
DTRules does not include random number generators or other non-deterministic operations. Every operation produces a predictable result.
Explicit Ordering
Decision table columns are evaluated left to right. When multiple rules can fire, the order is well-defined.
No External Dependencies
Rules operate only on the data provided. They don't access databases, files, or external services during execution.
Controlled Time
When rules need the current date/time, it's provided as input data. This ensures the same "today" is used consistently and can be controlled in tests.
Trace and Debug
DTRules provides comprehensive tracing to understand exactly how a decision was made.
Execution Trace
The trace shows:
- Which decision tables were executed
- Which columns matched
- Which actions were performed
- How values changed
# Enable tracing in Go CLI
./dtrules -rules path/to/rules -entry Main -trace
# Trace output shows:
# [DT] Entering: Determine_Eligibility
# [COND] Row 1: age >= 18 → true
# [COND] Row 2: income < threshold → false
# [COL] Column 2 matched
# [ACTION] set eligible = false
# [ACTION] set reason = "income"
# [DT] Exiting: Determine_Eligibility Entity Reports
After execution, you can generate reports showing the final state of all entities. This documents the complete decision context.
Balanced Tables
BALANCED decision tables enforce that all condition combinations are explicitly defined. This eliminates ambiguity about what happens in any scenario.
# For two boolean conditions, BALANCED requires 4 columns:
# (Y,Y), (Y,N), (N,Y), (N,N)
# The compiler will error if any combination is missing. Use BALANCED tables when completeness is required, such as regulatory rules where every case must be handled.
First vs All Tables
FIRST Tables
Execute only the first matching column. The order of columns matters and defines priority.
- Deterministic: leftmost match always wins
- Use for: priority-based rules, default/fallback logic
ALL Tables
Execute all matching columns from left to right. Multiple rules can fire for the same input.
- Deterministic: columns always execute in left-to-right order
- Use for: accumulating values, applying multiple rules
Best Practices for Determinism
1. Control Input Data
All data the rules need should be provided as input. Avoid reading from external sources during rule execution.
2. Use Balanced Tables for Critical Rules
When every scenario must be explicitly handled, use BALANCED tables. The compiler will catch missing cases.
3. Define Clear Column Ordering
For FIRST and ALL tables, document why columns are in a particular order. This helps maintainers understand the intended priority.
4. Include Today as Input
If rules depend on the current date, pass it as input data rather than using a built-in "now" function. This enables testing with specific dates.
// Good: today is input data
if birth_date + 18 years <= today then ...
// This enables testing:
// Test case: today = 2024-01-15
// Test case: today = 2023-12-31 5. Enable Tracing in Production
For auditable decisions, save the execution trace. This provides a complete record of how each decision was made.
Example: Reproducible Decision
// Input data
{
"applicant": {
"name": "John Doe",
"age": 35,
"income": 25000,
"citizen": true
},
"today": "2024-01-15",
"poverty_level": 15000
}
// Decision table: Determine_Eligibility
// Conditions:
// Row 1: age >= 18
// Row 2: income < poverty_level * 2
// Row 3: citizen = true
// With this input:
// - Row 1: 35 >= 18 -> true
// - Row 2: 25000 < 30000 -> true
// - Row 3: true = true -> true
// - Column (Y,Y,Y) matches
// - Actions: set eligible = true
// This decision can be reproduced:
// - Re-run with same input -> same result
// - Trace shows exactly why eligible = true
// - Audit record preserved Next Steps
- CHIP Tutorial - See deterministic rules in action
- Testing Rules - Write tests for your rules
- Decision Tables - Learn about table types