Kuga
Back

Envoy Business Unit Policy API

Led a five-engineer team building an enterprise insurance management platform — registry-driven, configurable, deployed on Kubernetes.

May 2, 2026

Problem

An insurance business unit needed a complete management platform: policy lifecycle from quote to claims, double-entry financial accounting, and an incentive engine flexible enough that business managers could write arbitrarily complex agent-incentive rules without involving engineers every time the rules changed.

Constraints

Enterprise client. Real money flowing through commission and incentive calculations — financial correctness was non-negotiable. Business rules changed often, so anything that required a code deploy per rule change would have failed. Production deployment had to be containerised and scalable from day one. I led a five-engineer team on this build, so the architecture also had to be legible enough for the team to extend correctly without me reviewing every line.

What I built

As team lead, I architected the platform and directed four other engineers building against it: a Django 5.1.6 REST API split into three modular apps — policy, finance, claims. 30+ controllers, 40+ models, 50+ endpoints. The financial layer implements double-entry accounting (invoices, payments, commissions, GL, journal entries). The incentive engine evaluates business-defined rule trees against aggregated agent performance and awards incentives automatically. Custom JWT middleware (7-day access / 30-day refresh) with role-based access control. Containerised with multi-stage Docker builds, shipped via Bitbucket Pipelines into Kubernetes.

Key decision

Configuration-driven over code-driven. Two patterns carry most of the weight: a **performance-field registry** that declares how each business metric maps to tables, joins, and aggregations; and a **recursive logic tree** stored as JSON that evaluates AND/OR/nested conditions against aggregated data. New metrics and new rules are config changes, not code deploys. The cost is a steeper initial investment in the framework — the payoff is that business changes don't queue behind engineering capacity.

Outcome

Production-deployed on Kubernetes via Bitbucket Pipelines CI/CD. 30+ configurable performance metrics live in the registry. Business managers define multi-condition incentive rules ("≥ $150K premium AND (≥ 15 policies OR ≥ $10K commission) → $2K reward") through configuration — the engine periodises (One-Time / Monthly / Quarterly), aggregates performance, evaluates the tree, and persists incentive records without any further engineering involvement.

Hindsight

The registry + logic-tree pattern is reusable beyond insurance — sales comp, KPI bonuses, channel partner programs all fit the same shape. If I built it again I'd extract the engine into a standalone library from day one, with the insurance domain as one consumer rather than letting it own the abstractions. I'd also wire up an evals harness for the rule engine early — the kind of business logic where "looks right in dev" and "is correct on every edge case under aggregation" can quietly diverge.

Architecture notes

Three patterns do most of the work in this system: the registry, the logic tree, and the query builder. They compose into a configuration layer that lets business users describe what they want without engineers translating it into code.

Registry-driven performance metrics

Each business metric — total premium, count of policies, agent commission — is declared once in a Python registry that says where the data lives, what to join, what to aggregate, and what filters are valid:

{
  "parameter": "issued_policy_premium_amount",
  "base_table": "crmp_issued_policies",
  "field": ["premium_amount"],
  "agg": "sum",
  "joins": [
    { "table": "crmp_policy_base",
      "on": "crmp_issued_policies.policy_base_id = crmp_policy_base.id" },
    { "table": "core_users",
      "on": "crmp_issued_policies.sales_agent_id = core_users.id" },
  ],
  "agent_field": "crmp_issued_policies.sales_agent_id",
  "filters": ["risk_type_id", "product_id", "policy_effective_date",
              "role_id", "agent_id"],
}

Adding a new metric is one registry entry — no controller changes, no serializer changes, no migration. The aggregation layer reads the registry at evaluation time and builds the SQL.

Recursive logic tree evaluation

Business rules are JSON trees. The evaluation engine recurses through them, short-circuiting on AND, finding a winner on OR, and bottoming out on leaf conditions that compare aggregated metrics to thresholds:

def evaluate_logic_tree(tree, performance_data, default_reward=None):
    if "logic" in tree:
        results = [
            evaluate_logic_tree(c, performance_data, default_reward)
            for c in tree["conditions"]
        ]
        if tree["logic"] == "AND":
            if all(r[0] for r in results):
                rewards = [r[2] for r in results if r[2] is not None]
                return True, [r[1] for r in results if r[1]], (max(rewards) if rewards else default_reward)
            return False, None, 0
        if tree["logic"] == "OR":
            for matched, cond, reward in results:
                if matched:
                    return True, cond, reward
            return False, None, 0
    # leaf condition
    return evaluate_leaf(tree, performance_data, default_reward)

This shape supports arbitrarily nested rules — "premium ≥ X AND (policies ≥ Y OR commission ≥ Z)" — without any new evaluator code per rule.

Dynamic query builder

The aggregation layer pulls every field referenced in a rule tree, looks each one up in the registry, and constructs the SQL on the fly through a fluent service (mServices) — selects, joins, agent filter, period filter, aggregation. One round trip per metric, indexed columns, no hand-rolled queries.

Why this composition

Once the three patterns exist, the system has a property most internal business platforms lack: business rule changes don't enter the engineering backlog. New metric → registry entry. New rule → JSON tree edit. New period type → one function. The engineering team owns the framework; the business owns the rules that run on it.

Tech stack

Python 3.13Django 5.1.6Django REST FrameworkMySQLdjangorestframework-simplejwtpydanticgunicornDocker (multi-stage)Bitbucket PipelinesKubernetes