guardrail-sim
byJeff Green

ADR 001: Policy Engine Library

Use json-rules-engine for deterministic policy evaluation

ADR 001: Policy Engine Library

Status: Implemented

Date: December 29, 2025

Context

We need a policy engine that can evaluate discount rules, shipping constraints, and promotional logic. The engine must be:

  • Deterministic (same input = same output)
  • Auditable (we can trace why a decision was made)
  • Fast (sub-100ms evaluation)
  • Extensible (custom rules without code changes)

Decision

Use json-rules-engine (Node.js) for deterministic policy evaluation.

Alternatives Considered

OptionProsCons
json-rules-engineBattle-tested, active maintenance, JS-native, good docsNo built-in UI, JSON-only rules
Drools (Java)Enterprise-grade, powerful rule languageJVM dependency, overkill for MVP
node-rulesSimple APILess active, fewer features
Custom implementationFull controlTime sink, bugs, maintenance burden
LLM-based evaluationFlexible, natural language rulesNon-deterministic, slow, expensive, unauditable

Rationale

  • json-rules-engine is the most popular JS rules engine (~1.5k GitHub stars)
  • Supports nested conditions, priority ordering, and custom operators
  • Deterministic evaluation is critical — same input must produce same output
  • JSON rules are human-readable and version-controllable
  • Active maintenance (last release within 6 months)

Consequences

  • Rules must be expressed in JSON schema (not natural language)
  • Need to build our own rule builder UI
  • Complex rules may require custom operators

Example Usage

import { Engine } from 'json-rules-engine';
 
const engine = new Engine();
 
engine.addRule({
  conditions: {
    all: [
      { fact: 'requested_discount', operator: 'greaterThan', value: 0.15 },
      { fact: 'customer_segment', operator: 'notEqual', value: 'platinum' },
    ],
  },
  event: {
    type: 'escalate',
    params: {
      message: 'Discounts over 15% for non-platinum customers require approval',
    },
  },
  priority: 10,
});
 
const result = await engine.run({
  requested_discount: 0.18,
  customer_segment: 'gold',
  order_value: 5000,
});

On this page