Katkat

Design a Rule Evaluation Engine for Spending Policy Violations

RipplingSoftware Engineer · Technical Screen

Last updated

This problem tests your ability to design a scalable, extensible rule engine that evaluates financial transactions against dynamic spending policies.

Problem Statement

Design a software component that evaluates incoming transaction requests against a set of spending policies to determine if the transaction should be 'APPROVED' or 'DENIED'.

Requirements

  1. Policy Structure: Policies consist of conditions (e.g., amount > 500, category == 'Travel', merchant == 'Amazon').
  2. Dynamic Evaluation: Policies should be configurable without requiring a full system deployment.
  3. Transaction Context: A transaction includes transaction_id, user_id, amount, currency, category, and merchant_name.
  4. Performance: Evaluation must be performed in near real-time (latency < 100ms).

Task

Define an interface or class structure that allows for defining, storing, and evaluating these rules. You must implement an evaluation function that processes a transaction against a list of active rules. Consider how to handle conflicting rules (e.g., a whitelist and a blacklist policy).

Interface

class Rule:
    # Define structure for a rule
    pass

def evaluate_transaction(transaction: dict, rules: list[Rule]) -> str:
    # Returns 'APPROVED' or 'DENIED'
    pass

Example

  • Input: Transaction {'amount': 600, 'category': 'Travel'}. Rules: [Rule('amount < 500'), Rule('category == Travel', action='DENY')]
  • Output: 'DENIED' (due to the category-based policy).

Constraints

  • Assume rules are stored in a database or cache.
  • Consider how to handle rule dependencies or sequential order.

Hints

Hint 1

Represent each rule as a collection of predicates that can be serialized (e.g., JSON) to support dynamic loading from a database.

Hint 2

Use the Strategy Pattern or an expression evaluation tree to decouple the transaction evaluation logic from specific rule definitions.

Hint 3

Think about the order of operations: should you evaluate 'Deny' rules before 'Allow' rules, or stop at the first matching violation?

Follow-up questions

  • How would you optimize the engine if you have 10,000+ rules to evaluate per transaction?
  • How would you implement an 'Audit Log' for the engine so that users can understand why a specific transaction was denied?
  • How would you handle rules that depend on historical data, such as 'limit spending to $1000 per rolling 30 days'?