BACK TO THE BLOG

Why I Use Go (And You Probably Should Too)

Actuarial Engineering#go#actuarial#insurance#simulation#software
TL;DR

From spreadsheet purgatory to compiled bliss — why Go dominates for actuarial systems. Concurrency with one keyword, single static binaries, type safety that catches bugs at compile time, and a standard library that's enough. For when Python's too slow and Excel's too fragile.

Every actuarial student learns the same workflow. You build your model in Excel, validate it in R, and present it in PowerPoint. If you're feeling fancy, maybe you write some Python. And you spend your entire career praying the spreadsheet doesn't corrupt itself.

I went through that pipeline. I wrote thousands of Excel formulas. I built R models that crashed with "1 NA" and no further explanation. I debugged NumPy code in Jupyter notebooks at 3 AM. And at some point I realized: this is insane. We're doing math that matters — pricing insurance policies, calculating reserves, determining solvency — and we're doing it in tools that weren't designed for any of this.

That's when I started writing Go.

The Speed Difference Is Embarrassing

Go compiles to native machine code. That means simulations that take minutes in Python or hours in R run in seconds. There's no interpreter overhead. No GIL bottleneck. No hidden type coercion that silently corrupts your results.

Here's a concrete example. In my reserving prototype, the Go version ran as fast as the Python equivalent using NumPy — and NumPy is already written in C. But Go used less memory, had zero dependencies, and didn't break when I looked at it wrong.

func runSimulation(policies []Policy, scenarios int) []Result {
    results := make([]Result, len(policies))
    var wg sync.WaitGroup

    for i, policy := range policies {
        wg.Add(1)
        go func(p Policy, idx int) {
            defer wg.Done()
            results[idx] = simulatePolicy(p, scenarios)
        }(policy, i)
    }

    wg.Wait()
    return results
}

That's concurrent simulation. One go keyword. No thread pools. No multiprocessing boilerplate. No tears.

Concurrency That Doesn't Make You Cry

Need to simulate 10,000 policy paths in parallel? Run stress testing across a range of assumptions? Go's goroutines and channels make this trivial. Not "trivial if you have a CS degree." Trivial as in:

go calculateReserves(policyBatch)

That's the whole thing. Compare that to Python's multiprocessing module, which requires you to understand processes, queues, pickling, and the various ways your code will silently fail in production.

In R you'd probably just give up and go get coffee. In Go, it's one keyword.

One Binary, No Drama

Remember the last time you tried to share a Python script with a colleague?

"Install Python 3.10. Actually 3.11. Wait, what version do you have? Okay, create a virtual environment. Now pip install -r requirements.txt. It failed? Oh, that package doesn't support your OS. Try — you know what, just use Docker."

Go produces a single static binary. I can compile it on my machine, send it to anyone, and it runs. No runtime. No dependencies. No "works on my machine." No excuses.

For actuarial work — where you're often handing off models to people who aren't professional developers — this is a killer feature.

Type Safety Saves Sanity

Go is statically typed. This catches bugs at compile time instead of three hours into a Monte Carlo simulation.

func calculatePremium(age string, amount float64) float64 {
    return age * amount  // Compile error: can't multiply string
}

Python lets that explode at runtime. Go tells you you're wrong before you even run it. When your simulation takes hours to complete, you'll appreciate catching mistakes early.

The tooling is also just better. go test, go fmt, go mod tidy — all built in. No installing linters, formatters, or dependency managers separately. Go handles it like a responsible adult.

The Standard Library Is Enough

I rarely need third-party libraries in Go. The standard library covers math, file I/O, HTTP servers, JSON encoding, cryptography, time handling — everything I need for building actuarial systems.

package main

import (
    "encoding/json"
    "net/http"
)

type Premium struct {
    Age    int     `json:"age"`
    Amount float64 `json:"amount"`
}

func calculateHandler(w http.ResponseWriter, r *http.Request) {
    premium := Premium{Age: 35, Amount: 100000}
    json.NewEncoder(w).Encode(premium)
}

func main() {
    http.HandleFunc("/calculate", calculateHandler)
    http.ListenAndServe(":8080", nil)
}

That's a working web server for actuarial calculations. No Flask. No Django. No 47-line requirements.txt.

What This Means for Actuarial Work

I'm not saying drop everything and rewrite your entire toolkit in Go. Python is still better for quick prototyping and data exploration. R is still the king of statistical modeling. Excel still has its place for ad-hoc analysis and business presentations.

But when you need to build a system — something that processes millions of policies, runs complex simulations, and produces reliable results that non-developers can use — Go is the right tool. It's fast, safe, and boring in the best possible way. No surprises. No runtime errors at 2 AM. Just compiled code that does exactly what you told it to do.

I've been building with Go for a while now. My actuarial engine v-star processes a million policies in under 300ms. My reserving tool Actuworry does everything the enterprise tools do, without the six-figure license fee.

And I never have to debug an Excel macro again. That alone was worth the switch.