Katkat

Build an Expense Policy Rule Engine

RipplingSoftware Engineer · Technical Screen

Last updated

This question tests your ability to design a rule evaluation engine that processes structured expense data against a set of logical conditions, including support for basic operators.

Problem Statement

You are tasked with building a lightweight rule engine for an expense management system. The system evaluates whether an Expense object complies with a set of Rules.

Definitions

  1. An Expense is represented as a dictionary with keys: amount (float), category (string), and is_international (boolean).
  2. A Rule is represented as a dictionary with: field (the expense key to check), operator (one of '>', '<', '==', 'in'), and value (the threshold or set to compare against).

Task

Implement a function is_compliant(expense, rules) that returns True if the expense satisfies all rules in the provided list, and False otherwise.

Examples

# Example 1: Basic validation
expense = {"amount": 150.0, "category": "travel", "is_international": False}
rules = [
    {"field": "amount", "operator": "<", "value": 500},
    {"field": "category", "operator": "in", "value": ["travel", "office"]}
]
# Result: True (150 < 500 AND travel is in [travel, office])

# Example 2: Failing validation
expense = {"amount": 1200.0, "category": "software", "is_international": True}
rules = [
    {"field": "amount", "operator": "<", "value": 1000}
]
# Result: False (1200 is not < 1000)

Constraints

  • rules will contain between 0 and 50 rule objects.
  • The value for the 'in' operator will be a list of primitives.
  • Ensure the code handles missing fields gracefully (treat as False if a rule references a non-existent key).

Edge Cases

  • Empty rules list: The function should return True (no rules violated).
  • operator not recognized: The function should raise a ValueError.
  • Mismatched types (e.g., comparing string to int).

Hints

Hint 1

Use a dictionary mapping operator strings to lambda functions to avoid long if-else chains.

Hint 2

Access the expense value dynamically using `expense.get(rule['field'])` to safely handle missing keys.

Hint 3

Iterate through the rule list and use the `all()` function or a simple loop with an early return.

Follow-up questions

  • How would you modify the engine to support nested logic, such as an 'OR' operator between groups of rules?
  • If this engine were used in a high-traffic environment, how would you optimize the evaluation of thousands of rules per second?
  • How would you redesign the system if the rules were stored in a database and needed to be fetched and compiled into executable code at runtime?