Every actuarial platform migration is sold on speed and cost. Excel to Databricks, single-thread R to Spark, one vendor valuation system to another — the business case is always faster runs and lower spend. Those benefits are real; I've delivered them. But the sentence that should open every migration plan is different: a migration is a model-risk event. The moment a calculation moves platforms, you have two implementations of the same model, and any disagreement between them is a defect in at least one — you just don't know which.

I've lived this from several seats: building a VM-21 engine on Databricks/PySpark to replace legacy Excel runs, with a formal assessment of model appropriateness and limitations; migrating single-thread R experience-study code to sparkR/sparklyr pipelines; and working alongside the MG-ALFA and Milliman Integrate teams on an actuarial model transition. The technology differed each time. The governance questions did not.

Separate what you're moving from what you're changing

The single most valuable act of discipline is to split the migration into two ledgers. One ledger holds things that must not change: assumptions, methodology, the economic result. The other holds the things you are deliberately changing: the compute engine, data storage, orchestration, run frequency. Every observed difference between old and new gets charged to one ledger or the other, in writing.

Without this split, migrations drift into a dangerous habit: "while we're in here, let's also fix the lapse assumption." Now the parallel run can't isolate anything — implementation differences and assumption changes are confounded, and sign-off becomes a judgment call dressed as a reconciliation. Freeze the model, move it, prove equivalence, and only then change it. Assumption updates deserve their own governance cycle on the new platform.

The reconciliation hierarchy

"The totals match" is where reconciliation starts, not where it ends. Aggregate agreement can hide large offsetting errors — I have seen a portfolio-level reserve agree to four significant figures while two product cohorts were each wrong by material amounts in opposite directions. The hierarchy that actually earns sign-off runs in the other direction:

  • Seriatim first. Policy-level comparison across the full book, not a sample. On Spark this is cheap — comparing a billion rows is exactly what the platform is for. The distribution of per-policy differences tells you more than any total: a tight cluster at zero with a small tail of outliers points to edge-case logic; a small uniform drift points to a rounding or day-count convention.
  • Cohort second. Roll differences up by product, issue year, fund, and any dimension management reports on. This is where offsetting errors surface.
  • Aggregate last, with stated tolerances. Tolerances agreed before the parallel run starts — per policy, per cohort, in total — with a named owner for every breach. A tolerance negotiated after you've seen the differences is not a tolerance; it's an excuse.

Expect legitimate differences, and document them as such. Floating-point summation order alone will move the last digits when a calculation goes parallel: summing a million discounted cashflows in a different order gives a slightly different total, and chasing exact binary equality across platforms is a waste of a parallel run. The governance answer is not "make it identical"; it's "explain the difference, bound it, and show it doesn't matter at the decision level."

The new platform changes what governance can be

Here's the part the risk register usually misses: done properly, the migration leaves you with better governance than you started with, because the modern stack has controls the legacy estate could never offer.

On the Databricks builds I delivered, Delta Lake versioning meant every input table and every result set was reproducible as-of any run date — the "which spreadsheet did we use in Q3?" conversation ceases to exist. MLflow gave model versioning and performance tracking, so the answer to "what changed between these two runs?" is a diff, not an interview. Runs that took long enough to discourage sensitivity testing now finish fast enough that testing the basis ten ways before a committee meeting is routine — a 40% runtime reduction isn't only a cost line, it's a control improvement, because a model you can afford to re-run is a model you can afford to challenge.

But none of that arrives automatically. A notebook environment without discipline is Excel with better marketing — the same untracked copies, the same "final_v2_FIXED" culture, at larger scale. The governance artifacts have to be built in: version-pinned clusters, code review on calculation logic, immutable published result tables, and a documented model inventory entry that says what the model is for, what it assumes, and where it should not be used.

Appropriateness is a judgment, and someone has to make it

The formal model-appropriateness assessment I ran for the VM-21 engine asked questions no reconciliation can answer: Is this methodology still fit for the products on the books today? Which limitations of the legacy model did we faithfully carry over — and should we have? Where does the model's validity end? Writing the limitations section honestly is uncomfortable, because a migration team is institutionally motivated to declare victory. That is exactly why the assessment should be written by someone whose success is not measured by the go-live date.

A migration ends not when the new platform runs, but when the old one can be switched off and nobody objects — because the reconciliation evidence, the documented differences, and the appropriateness assessment together make the case that the model survived the move intact. That evidence pack is a deliverable, and it deserves the same craft as the engine itself.

If you're planning or mid-way through a model migration and the reconciliation, documentation, or model-risk framework needs an experienced hand — that's work I do. Get in touch.