Design a Rules Engine for Corporate Credit-Card Expense Review
Context
You are designing a rules engine that evaluates corporate credit-card expenses and flags items for manager review. The engine will be used by an expenses API serving many companies (multi-tenant). Expenses are provided as a list of dictionary objects with string keys and string values.
Assume each expense has identifiers and basic fields like amount, currency, expense_type, vendor_type, merchant, transaction_date, and optionally trip_id.
Initial Base Rules (must be supported)
-
No restaurant expense over $75.
-
No airfare expenses.
-
No entertainment expenses.
-
No expense over $250.
Inputs
-
expenses: list<dict<string, string>> (string-typed fields that must be parsed/validated).
-
rules: list
<Rule>
(you define the rule types and semantics).
Function to Propose
-
evaluateRules(rules: list
<Rule>
, expenses: list
<Expense>
) -> ReturnType
-
The ReturnType must be stable/backward compatible for clients across many companies.
Design Goals
-
Support 100+ rules selected per manager/team; allow adding new rules and rule creation via an API without breaking clients.
-
Specify evaluation semantics:
-
Priority/ordering of rules
-
Combinators (AND/OR/NOT)
-
Short-circuiting vs exhaustive evaluation
-
Conflict resolution across rules
-
Provide explainability/audit:
-
Per-violation reason, rule metadata, timestamps, and who/what policy triggered the violation
-
Address multi-tenant isolation, authorization, observability, idempotency, deterministic ordering, and resilience to partial failures
-
Define core data models and parsing/validation strategy:
-
Expense, Trip, Rule, Policy, RuleResult, EvaluationSummary
-
How to parse/validate amounts, categories, vendor types from string-typed inputs
Extension: Aggregate (Group-By) Rules
Support trip-level rules such as:
-
(a) A trip’s total expenses must not exceed $2000
-
(b) Total meal expenses per trip must not exceed $200
In the sample dataset, assume trip "002" violates both.
Requirements:
-
Extend the engine to evaluate both per-entity (expense) and aggregate (trip) rules in one pass.
-
Describe aggregation strategy, caching, and incremental updates.
-
Propose a backward-compatible ReturnType that surfaces both expense-level and trip-level violations, including:
-
entity type (expense/trip), identifiers (expense_id/trip_id)
-
violated rule codes, severity, human-readable messages
-
machine-readable fields (e.g., observed_value, threshold, currency)
-
pagination (cursor-based)
Product Questions to Clarify
-
Policy scoping (company/team/user) and overrides/precedence
-
Currency/FX handling, locales, tax/tip treatment for meal caps, per-diem vs per-transaction limits
-
Category taxonomy (expense_type, vendor_type), who classifies, handling unknown/mislabeled categories
-
Time windows (per trip/day/month), receipt requirements, exception workflows and approvals, SLAs/notifications
-
Versioning and rollout (A/B, canary), API rate limits, and return-type compatibility strategy over time