Skip to content
QSWEQB
hardDesignCase StudyMidSeniorStaff

How would you build a rating engine, and how do you reproduce a quote you gave someone eighteen months ago?

Model the rating algorithm as an ordered set of steps over versioned rate tables, pin every quote to the exact rate version and input snapshot that produced it, and keep eligibility and referral rules outside the price calculation so an underwriter decision never changes the number.

5 min readUpdated 2026-07-26

What the interviewer is scoring

  • Whether the design pins a quote to an immutable rate version rather than re-running today's rates against yesterday's inputs
  • Does the candidate separate the rating algorithm from the rate data, and explain why that separation is what makes rate changes deployable
  • Recognition that eligibility, referral and decline rules are a different concern from price and must not silently alter it
  • That they can position a statistical model as an input to the rating structure rather than a replacement for it
  • Whether they raise who is allowed to change a rate table, and what approval that change needs

Answer

The shape of a rating calculation

A rating engine turns a described risk into a premium by executing an ordered sequence of steps. The classic multiplicative structure starts from a base rate for the product and coverage, applies a relativity for each rating factor, and then applies adjustments that are not factors at all: minimum premium, optional cover charges, discounts and loadings, and finally taxes and levies that are calculated on the premium but are not premium.

The distinction that matters to the design is between the algorithm and the data. The algorithm is "base rate, then territory relativity, then vehicle group relativity, then no-claims discount, then apply minimum premium, then add optional covers, then apply insurance premium tax". It changes rarely and it is code. The data is the thousands of numbers those steps look up, and it changes constantly. If your rate values live in code, every price change is a release, and you have coupled the cadence of pricing to the cadence of engineering. Rate tables belong in data with their own lifecycle, and the engine loads them.

Rating factors are the attributes the tables are keyed on, and they are not the same thing as the data the customer gives you. A customer gives a postcode and a date of birth; rating uses a rating area and an age band. That mapping is itself versioned reference data, and forgetting it is a common source of irreproducibility, because rebanding a factor changes prices without any rate value changing.

Versioning that makes reproduction possible

The requirement to reproduce an eighteen-month-old quote exactly is not an audit nicety. It arises when a customer disputes a renewal price, when a complaint escalates, when a regulator asks how a price was arrived at, and when you are trying to explain a loss ratio movement to an actuary. It has to be a first-class property.

Reproduction fails if you try to achieve it by re-running the engine. Even with the right rate tables you will have drifted: reference data has been rebanded, an external enrichment service now returns a different credit or vehicle attribute for the same input, a rounding rule has been tidied. The reliable approach is to store the full result and the full input, and treat re-execution as a verification tool rather than the source of truth.

// Stored against the quote. Everything needed to explain the price,
// without needing the engine to still behave the way it did.
{
  "quoteId": "Q-8841207",
  "quotedAt": "2025-01-19T10:42:11Z",
  "rateSetId": "MOTOR-COMP-2024-11-A",     // immutable, published, never edited
  "engineVersion": "4.7.2",
  "inputSnapshot": { "ratingArea": "M14", "ageBand": "30-34", "ncdYears": 5 },
  "externalData": { "vehicleLookup": { "provider": "…", "responseId": "…" } },
  "steps": [
    { "step": "baseRate",        "value": 412.00 },
    { "step": "ratingArea.M14",  "factor": 1.18, "runningTotal": 486.16 },
    { "step": "ncd.5yr",         "factor": 0.72, "runningTotal": 350.04 }
  ],
  "netPremium": 350.04
}

The step trace is the part people leave out and then wish they had. A single premium figure tells you nothing when someone asks why the price moved; a factor-by-factor trace answers that question and doubles as the explanation an underwriter or a complaints handler needs.

A rate set must be immutable once published and identified by both an identifier and an effective date range. Editing a published rate set is the defect that destroys reproducibility most often, because it is so easy to justify: someone fixes a typo in one cell and every quote priced under that set becomes unexplainable. Corrections are new versions.

Referral rules are not pricing

Underwriting rules answer a different question from rating. Rating asks how much; underwriting asks whether, and on what terms. Those rules divide roughly into eligibility or knock-out rules that decline the risk outright, referral rules that route the case to a human before it can be bound, and rules that attach a condition, an increased excess or an exclusion.

Keep them in a separate evaluation from the premium calculation, and have them emit decisions rather than mutate the price. When a rule needs to change the price, it should do so by adding a named loading that appears in the trace, not by reaching into an intermediate value. Two things go wrong when this boundary is soft. The premium becomes unexplainable, because part of it came from a rule nobody thinks of as pricing. And the referral queue becomes unmanageable, because no one can measure the referral rate of an individual rule when rules and rating are tangled together. Instrument every rule with its own hit rate from day one; that number is what lets underwriting management decide which rules earn their keep.

Where a model sits

A statistical model, typically a generalised linear model in traditional pricing and increasingly a gradient-boosted model, does not replace the rating structure. It produces an estimate of expected cost, usually decomposed into claim frequency and claim severity, and that estimate feeds the structure as a technical or risk price.

What sits on top of the technical price is a commercial layer: target loss ratio, expenses, commission, capital cost, competitive position and renewal treatment. The gap between the technical price and the charged price is where the commercial decisions live, and keeping it visible as a distinct step is what allows anyone to answer whether a segment is unprofitable because the model is wrong or because the market forced the price down.

Two constraints on the model are worth knowing about. First, in filed-rate markets, most notably US personal lines, the rating structure and its factors are filed with and approved by state regulators before use, which puts real limits on how freely a model output can drive a price and on how quickly it can change. Second, in the UK the FCA's general insurance pricing practices rules require that a renewing customer is not offered a price higher than an equivalent new customer would be quoted through the same channel, which constrains renewal pricing directly rather than the model itself. Practice varies enough by market that the honest answer in an interview names the constraint you have worked under and says so.

Both constraints push in the same direction architecturally: the model output should enter the rating structure as an identifiable, versioned, auditable component, not be smeared across the factor tables where nobody can see it.

Likely follow-ups

  • How do you roll out a rate change by effective date without disturbing quotes already in flight?
  • What is the difference between a technical price and the price you actually charge, and where does the gap live in the system?
  • How would you test a rate change before release, given you cannot unit-test a premium into existence?
  • A referral rule fires on twelve per cent of quotes and underwriting cannot cope. What do you change first?

Related questions

rating-engineunderwritingversioningpricingreferral-rules