dispatch / mercury-haskell-production-engineering
Mercury Runs $248B Through 2 Million Lines of Haskell — and Most of Their Engineers Learned It on the Job
Ian Duncan's brutally honest account of what it actually takes to keep a massive Haskell codebase alive when half your coworkers have been writing the language for six months.
There's a certain kind of tech blog post that makes you wince before you click. The title says something like "How We Scaled to 10 Billion Requests With Rust" and you just know it's going to be 3,000 words of humblebragging wrapped in a thin veneer of technical content, ending with a hiring pitch.
Ian Duncan's piece on the Haskell Blog is the exact opposite of that. It opens with him admitting that most people's instinct upon hearing "2 million lines of Haskell maintained by engineers who learned it on the job" is to recoil in horror — and then spends the next several thousand words explaining why that instinct is simultaneously right and wrong.
Mercury is a fintech company that processed $248 billion in transaction volume in 2025 on $650 million in annualized revenue, serving over 300,000 businesses. They're currently in the process of obtaining a national bank charter. They have around 1,500 employees. And yes, most of their backend engineers had never written a line of Haskell before joining.
The article isn't a victory lap. It's a field manual.
Types as Institutional Memory
The single most valuable insight Duncan offers is this: Haskell's type system is less useful as a correctness prover than as a mechanism for preserving operational knowledge after the people who discovered it leave the building.
In any fast-growing company, half your coworkers will always have less than a year of experience. (Duncan cites Patrick McKenzie's observation about this, and it's one of those truths that feels obvious once stated but changes how you think about everything.) The things senior engineers know become "institutional dark matter: load-bearing, but invisible to most of the people around you."
The type system is the antidote. When you encode an invariant — "you must flush the audit log inside the transaction, not after it" — as a type that makes the wrong thing impossible, you've turned tribal knowledge into a compiler-enforced contract. The wiki page can rot. The senior engineer can leave. The compiler stays.
This isn't academic. It's pure operational pragmatism disguised as type theory.
Purity Is a Boundary, Not a Property
Duncan's hot take on purity is worth quoting at length: behind every "pure" function in bytestring, text, and vector lies "a cheerful little hellscape of mutable allocation, buffer writes, unsafe coercions, and other behavior that would alarm you if you discovered it in a junior engineer's side project."
The goal isn't to avoid mutation entirely — that's not a serious proposition for most real systems. The goal is to contain mutation, make the containment legible, and verify that it stays contained. "Purity is a boundary you try to maintain" is a much more useful mental model for an engineer than "Haskell is pure."
This applies everywhere: database connection pools, HTTP client circuit breakers, caches with concurrent mutable maps. The pattern recurs — dangerous things are tolerable when fenced in, carefully exposed, and hard to misuse.
The Monoid Pattern Nobody Talks About
One of the most practically useful sections describes how Mercury uses the Monoid instance for middleware composition. Their interceptor stack is literally:
appTemporalInterceptors = mconcat [
retargetingInterceptor, otelInterceptor, sentryInterceptor,
sqlApplicationNameInterceptor, loggingContextInterceptor,
statementTimeoutInterceptor, teamNameInterceptor,
clientExceptionInterceptor, workflowTypeNameInterceptor
]Each interceptor is defined in isolation by a different engineer who only needs to think about one concern. Composition is (<>). There's no hidden wiring, no coordination tax. Fifteen engineers each write one piece, mconcat glues them together, and nothing breaks because the algebra guarantees composition is correct by construction.
This is the kind of thing that sounds like category-theory wankery until you've spent a Tuesday afternoon debugging a hand-rolled interceptor chain in Python where the ordering was "documented" in a Slack thread from nine months ago.
The Honest Parts
What makes the article genuinely useful — rather than just impressive — is Duncan's willingness to describe what doesn't go in the types, what isn't beautiful, and where the language and ecosystem fall short.
He admits that unsafePerformIO is used in libraries everyone depends on daily. He points out that an alarming number of Hackage libraries have few or no tests, and that the implicit argument "if it compiles, it works" should not be trusted — especially for IO-heavy code or anything touching external systems. He notes that Haskell simply doesn't have the batteries-included development experience of Next.js or Rails, and that the error messages "occasionally read like the screed of a madman who knows exactly what is wrong and deeply resents having to explain it to you."
He also warns explicitly about the cultural risk: "Haskell attracts idealists. This is mostly a strength... But idealism, left unchecked, becomes a production liability. The engineer who wants to rewrite the database layer using a novel type-level encoding of relational algebra is not helping you ship features."
What This Actually Proves
The conventional narrative about niche languages in production is that they're either a secret weapon (the Jane Street/OCaml story) or an impending disaster (the "nobody can hire for this" story). Mercury's experience complicates both.
The hiring concern, Duncan argues, is overstated. Their CTO has said publicly that backend Haskell engineer is the easiest role to hire for at Mercury — there's more demand for Haskell jobs than the market provides, which inverts the usual recruiting dynamic. And they've built a training program that gets engineers productive in Haskell in six to eight weeks, whether they've written it before or not.
The real variable isn't the language. It's whether the organization treats the language as a tool or a religion. Mercury treats Haskell as a power tool — one that happens to be exceptionally good at preserving institutional knowledge through hypergrowth, provided you resist the urge to use every feature on every problem.
There's a line near the end that captures the whole thing: "The goal is not moral purity. The goal is to avoid discovering, during an incident, that half the system's assumptions existed only as oral tradition."
That's not a Haskell insight. It's a production engineering insight that happens to be expressed in Haskell. And it's the most useful thing I've read about language choice in years.
Read Ian Duncan's full article on the Haskell Blog. HN discussion here — 296 points, 130 comments.