Mercury is a fintech that processed $248 billion in transaction volume in 2025 on $650 million in annualised revenue, serves more than 300,000 businesses, employs around 1,500 people, and is, at the time of writing, applying for a national bank charter from the OCC. Its engineering organization is built around roughly two million lines of production Haskell. Most of the engineers who work on that codebase had not written a line of Haskell before joining the company.
That last sentence is the one that conventionally provokes recoil. The recoil is the wrong response. The interesting structural fact in Ian Duncan's March 2026 Haskell Blog post — published as the inaugural piece in a Haskellers from the trenches series, and the basis for a 273-point Hacker News thread on May 3 — is not that two million lines of Haskell scale. It is the operational practice a fintech-at-scale has built around the language to make the maintenance arithmetic survivable. Duncan, an engineer on Mercury's Stability team and a roughly two-decade Haskell user, names the practice specifically. The article below is an attempt to translate that practice into the parts most worth borrowing if you do not happen to be running a fintech in Haskell.
What kind of company puts a quarter-trillion through Haskell
The named numbers do most of the throat-clearing. Beyond the figures already cited, Mercury survived the Silicon Valley Bank collapse in March 2023 (the second-largest bank failure in US history at the time) by absorbing 8,700 new customers and roughly $2 billion in deposits across five days, of whom 95% remained as customers. Whatever else the engineering organization has been doing, it has done it under the kind of stress test that does not appear on roadmaps.
Duncan's framing of why the architecture survived that period is the load-bearing claim of his post, and it deserves to be quoted in the spirit it is offered. "Elegance is pleasant," Duncan writes, "but keeping your business alive is compulsory." The article is not a celebration of Haskell's beauty. It is a description of the operational discipline a fintech has wrapped around the language to keep the maintenance bill from compounding faster than the headcount.
Compiler as custodian
The central practical claim, and the one that justifies the article's title above, is that in a fast-growing organization the type system's main value is not as a correctness proof. It is as a custodian of operational lore. As Duncan puts it, "things people knew walk out the door with them unless you have written them down somewhere. Ideally, you have written them down in a form that the compiler can read, because the compiler is much more disciplined than the average wiki page."
The example he uses to operationalise this is small enough to quote in full. Consider a system in which certain side effects (sending a notification, publishing an event) must happen transactionally with a database write, never before, never after, never separately. The naïve approach is to leave the engineer two functions and instructions to call them in the right order:
-- Please use this one, not the other one
writeWithEvents :: Transaction -> [Event] -> IO ()
-- Don't use this directly (but we can't stop you)
writeTransaction :: Transaction -> IO ()
publishEvents :: [Event] -> IO ()
This is the wiki-page-as-architecture pattern. It works, Duncan observes, "until it doesn't," and the until-it-doesn't tends to arrive on a Friday afternoon when the person who wrote the wiki page is on vacation. The Mercury approach is to restructure the types so that the only available door is the safe one:
data Transact a -- opaque; cannot be run directly
record :: Transaction -> Transact ()
emit :: Event -> Transact ()
-- The *only* way to execute a Transact: commit and publish atomically
commit :: Transact a -> IO a
The opaque Transact type with a single execution path through commit removes the choice that the wiki-page version was depending on. A new engineer's question of how do I write a transaction is answered by the type system the same way it would have been answered by the senior engineer who is now three teams away. Patrick McKenzie's well-known observation about fast-growing companies, that at a 2× annual growth rate half of one's coworkers will always have less than a year of experience indefinitely, is what makes this trade favourable. Documentation is excellent when written, and walks out of the building the day the author transfers teams. A type signature is harder to forget to update, because forgetting causes the build to fail.
Where Mercury draws the line
The trade has limits, and Duncan is unusually direct about where they are. He frames the spectrum as one between cathedrals and tents. The cathedral end encodes everything in types — illegal states are unrepresentable, refactors take weeks because every business-rule change threads through fifty modules, and new engineers stare at the type signatures and quietly begin discussing their career options with a therapist. The tent end encodes nothing — the code is easy to change, the system works because the people who built it remember what the strings mean, and when those people leave it stops working and nobody knows why.
Mercury sits in the middle, with explicit principles. "Encode invariants that protect against silent corruption", Duncan writes: the cases where a violation produces wrong data without raising an immediate error. "Use runtime checks for invariants that fail loudly", where a violation produces an obvious 500 response or a JSON-boundary type mismatch. "Resist the urge to model your entire domain in types", because the domain is messy, has grandfather clauses, contradictory rules, and special behavior for three customers since 2018. The hairy type-level machinery exists where the consequences of getting it wrong involve money going to the wrong place. The complexity is encapsulated, with a small surface API exposing five normal-looking functions to the rest of the codebase.
The corollary, also Duncan's, is the hire he warns against. "Haskell attracts idealists", he observes: engineers who refuse to merge code that uses String rather than Text in throwaway scripts, who treat every design discussion as an opportunity to advocate a total rewrite. The pragmatic counter, raised by a long-time Haskell-hiring manager in the HN thread on Duncan's piece, is that the most useful Haskell hire is "somebody with lots of experience building things who ended up coming to Haskell later". Mercury reportedly hires this way, and runs a six-to-eight-week training program for engineers who arrive without Haskell on their CV at all.
What the practice bought them, operationally
Two specific moves stand out in Duncan's account. The first is durable execution, via Temporal — Mercury replaced fragile cron-and-database state machines with workflow code whose failure semantics are platform-handled (replay, retry, timeout, cancellation). Mercury open-sourced its hs-temporal-sdk, which wraps Temporal's official Rust Core SDK via FFI and provides a Haskell-native API. The dovetail with Haskell's pure-core / impure-shell discipline is structural: a Temporal workflow is a pure function over its event history, which is the same constraint Haskell already imposes on pure code.
The second move is observability via records of functions, a pattern in which library APIs are exposed as records whose fields are functions, which the caller can wrap, instrument, mock, or replace without touching the library's source. The canonical positive example Duncan cites is the persistent library's SqlBackend type, which made it possible to add OpenTelemetry tracing spans around every database operation by wrapping fields in a few lines of code, with no fork required. The corollary plea, addressed to library authors, is to take hs-opentelemetry-api as a dependency, expose .Internal modules with stability warnings rather than forcing forks, and never log directly from library code; let the application route logs.
Both moves are downstream of the same containment principle that runs through the post: dangerous things are tolerable when they are fenced in, carefully exposed, and hard to misuse.
Where else this practice has worked
Mercury is the largest production-Haskell deployment to publicly publish its engineering practice this clearly, but it is not alone in the operational scale. The shops below are the verifiable comparison set.
| Shop | Domain | Approx scale | Notable pattern |
|---|---|---|---|
| Mercury (fintech, US/global) | Banking services for businesses | ~2M LoC; ~1,500 employees; $248B 2025 transaction volume | Compiler-as-custodian + hs-temporal-sdk durable execution + records-of-functions observability |
| Standard Chartered, Core Strats group | Investment-bank trading & analytics | 40+ developers; in-house Mu Haskell dialect maintained for 11+ years | Whole-program compilation, strictness by default, computation-serialisation for parallel execution |
| Bellroy (e-commerce, AU) | Retail product imagery & checkout pipeline | Haskell in production since 2019; monorepo with continuous deployment | Lambda + DynamoDB cost-cut; Nix/Hydra CI/CD; configuration-as-code with golden-test harness |
IOG / Intersect, cardano-node |
Cardano blockchain consensus | Hundreds of thousands of lines; secures billions of dollars in value | Formal Agda specifications drive Haskell implementation; multi-era consensus as composed external components |
The shapes differ. Mercury's is the most operations-engineering-heavy, Standard Chartered's the most language-customised, IOG's the most formally-verified, Bellroy's the most ergonomically-mainstream. All four organizations have arrived at variations of the same trade. The type system is treated as load-bearing infrastructure for the institutional knowledge that would otherwise live in wikis, in retiring engineers' heads, or nowhere.
Coda
The interesting takeaway is not use Haskell. The interesting takeaway is the operational shape of what Mercury has actually built — a discipline that uses the type system to encode operational lore, isolates dangerous machinery behind narrow interfaces, treats observability as a first-class library-design concern, and recruits builders rather than enthusiasts. None of those four practices requires Haskell. Each is sharper-edged in Haskell because the language lets the trades be enforced rather than negotiated. The case study is operationally specific. The pattern is portable. The next fintech at this scale will be running a different language in the same shape, or a worse-functioning version of the same business.

