Skip to main content

Quick Wins

Enable Caching

Cache expressions and paths (enabled by default)

Reuse Engine

Create once, use many times

Order Rules

Put fastest checks first

Batch Evaluate

Evaluate multiple rules at once

Caching

Expression Cache

Caches evaluated expressions for repeated use.
const engine = createRuleEngine({
  enableCache: true,      // Default: true
  maxCacheSize: 1000      // Default: 500
});

// First call - cache miss
engine.evaluateExpr(rule, data); // ~2ms

// Second call - cache hit
engine.evaluateExpr(rule, data); // ~0.1ms (20x faster!)
Benefits:
  • ✅ 10-20x faster for repeated rules
  • ✅ Automatic LRU eviction
  • ✅ Memory efficient
When to disable:
  • Highly dynamic rules
  • Memory constraints
  • Rules evaluated once

Path Cache

Caches path resolutions.
// Cached automatically
engine.resolvePath(data, 'user.profile.name'); // Cache miss
engine.resolvePath(data, 'user.profile.name'); // Cache hit

// Clear when data structure changes
engine.clearCache();

Rule Ordering

Put fastest checks first to fail fast.

Performance Hierarchy

  1. Fastest: Simple comparisons (eq, neq, gt, lt)
  2. Fast: Array checks (in, notIn)
  3. Medium: String operations (contains, startsWith)
  4. Slow: Regex patterns (regex)
  5. Slowest: State changes (changed, increased)

Bad vs Good

  • ❌ Bad - Slow First
  • ✅ Good - Fast First
// Regex first (slow!)
const rule = {
  and: [
    { regex: ['email', '^[a-z]+@example\\.com$'] },  // 1ms
    { eq: ['status', 'active'] },                     // 0.01ms
    { gte: ['age', 18] }                              // 0.01ms
  ]
};
// Total: ~1ms even if status check would fail

Reuse Engine

Create once, reuse everywhere.
  • ❌ Bad
  • ✅ Good
// DON'T create new engine each time
function checkUser(user) {
  const engine = createRuleEngine(); // Expensive!
  return engine.evaluateExpr(rule, user);
}

Batch Operations

Use StatefulRuleEngine.evaluateBatch() for multiple rules.
// Inefficient
rules.forEach(rule => {
  statefulEngine.evaluate(rule.id, rule.expr, data);
});

// Efficient - single pass
const results = statefulEngine.evaluateBatch({
  'rule1': rule1Expr,
  'rule2': rule2Expr,
  'rule3': rule3Expr
}, data);
Benefits:
  • Single context preparation
  • Shared path resolutions
  • Better cache utilization

Minimize Depth

Flatten nested rules when possible.
  • Nested (Slower)
  • Flat (Faster)
{
  and: [
    { and: [
      { and: [
        { eq: ['a', 1] },
        { eq: ['b', 2] }
      ] },
      { eq: ['c', 3] }
    ] }
  ]
}

Monitoring

Get Metrics

const metrics = engine.getMetrics();

console.log({
  total: metrics.evaluations,
  errors: metrics.errors,
  avgTime: metrics.avgTime.toFixed(2) + 'ms',
  hitRate: (metrics.cacheHits / metrics.evaluations * 100).toFixed(1) + '%'
});

Cache Stats

const stats = engine.getCacheStats();

console.log({
  expressionCache: `${stats.expression.size}/${stats.expression.maxSize}`,
  pathCache: `${stats.path.size}/${stats.path.maxSize}`
});

Benchmarking

const rule = { /* your rule */ };
const data = { /* your data */ };

console.time('evaluation');
for (let i = 0; i < 10000; i++) {
  engine.evaluateExpr(rule, data);
}
console.timeEnd('evaluation');

const metrics = engine.getMetrics();
console.log('Avg:', metrics.avgTime.toFixed(3) + 'ms');
console.log('Cache hit rate:', (metrics.cacheHits / metrics.evaluations * 100).toFixed(1) + '%');

Real-World Optimization

  • Before
  • After
// Slow: 5ms per evaluation
const slowRule = {
  and: [
    { regex: ['description', '\\bimportant\\b.*\\burgent\\b'] },
    { contains: ['tags', 'priority'] },
    { eq: ['status', 'active'] },
    { gte: ['priority', 5] }
  ]
};

Best Practices

const engine = createRuleEngine({
  enableCache: true,
  maxCacheSize: 1000
});
// Singleton pattern
export const engine = createRuleEngine();
// Fast → Slow
rules.and(
  rules.eq('status', 'active'),    // Fast
  rules.gte('age', 18),            // Fast
  rules.regex('email', pattern)    // Slow
);
statefulEngine.evaluateBatch(allRules, data);
setInterval(() => {
  const metrics = engine.getMetrics();
  console.log('Performance:', metrics);
}, 60000);

Performance Checklist

  • Cache enabled
  • Reusing engine instance
  • Fast checks first in and rules
  • Slow checks last
  • Using batch operations
  • Monitoring metrics
  • Rules flattened (not deeply nested)
  • Clear cache when data schema changes