A Couple Million Lines of Haskell: Production Engineering at Mercury
Meta Description: Discover how Mercury scaled a couple million lines of Haskell in production — lessons in type safety, team growth, and real-world functional programming at scale.
TL;DR
Mercury, the fintech banking platform for startups, has built one of the largest known Haskell codebases in production — spanning a couple million lines. This article breaks down what that actually means, the engineering tradeoffs involved, the lessons learned, and what other teams can take away from running purely functional code at serious scale. Whether you're a Haskell enthusiast, a skeptical engineering manager, or just curious about unconventional tech stacks in fintech, there's something here for you.
Introduction: When "Unusual" Becomes "Impressive"
Most fintech companies reach for Java, Go, or Python when building their core banking infrastructure. Mercury chose Haskell — and not just for a microservice or two. Over the years, their engineering team has grown a codebase that now spans a couple million lines of Haskell, making it one of the most ambitious deployments of the language in any production environment, let alone one handling real money for hundreds of thousands of businesses.
This isn't a theoretical exercise. Mercury processes real transactions, manages real bank accounts, and operates under real regulatory scrutiny. The decision to bet on Haskell — and the story of scaling that bet — offers a rare, honest window into what production functional programming actually looks like.
Let's dig in.
What Is Mercury, and Why Does This Matter?
Mercury is a neobank founded in 2019, targeting startups and small-to-medium businesses. It offers business checking accounts, savings, credit cards, and treasury management tools. As of 2026, the company manages billions in deposits and serves hundreds of thousands of business customers.
The engineering team's choice to build on Haskell wasn't accidental or trendy. It was a deliberate architectural decision rooted in the belief that strong static typing and pure functional programming would reduce bugs in financial software — where a misplaced decimal or a race condition can have serious consequences.
That hypothesis, played out over millions of lines of code and years of production traffic, is exactly what makes Mercury's story worth examining.
The Scale: What "A Couple Million Lines of Haskell" Actually Means
To put the number in context:
| Codebase | Approximate Lines of Code | Language |
|---|---|---|
| Linux Kernel | ~28 million | C |
| Chromium | ~35 million | C++/JavaScript |
| Mercury (core backend) | ~2 million | Haskell |
| Standard Haskell project | 10,000–100,000 | Haskell |
Most Haskell projects in the wild are research tools, compilers, or small services. A two-million-line production Haskell codebase is genuinely unusual — and the engineering challenges that come with it are proportionally unique.
This scale introduces problems that don't exist at smaller sizes:
- Compilation times become a serious developer experience issue
- Onboarding new engineers unfamiliar with Haskell takes longer
- Tooling gaps that are tolerable in small projects become blockers
- Dependency management grows increasingly complex
- Refactoring — even with strong types — requires coordination at scale
Mercury's engineers have publicly discussed all of these challenges, and their solutions offer real lessons for any team operating at scale.
Why Haskell? The Technical Case for Functional Fintech
Strong Types as a Safety Net
Haskell's type system is not just "static typing" — it's a sophisticated tool for encoding business logic directly into the compiler. At Mercury, this means:
- Phantom types to distinguish between different kinds of monetary values
-
Newtype wrappers that prevent accidentally passing a
UserIdwhere anAccountIdis expected - Algebraic data types (ADTs) that make illegal states literally unrepresentable in code
In financial software, this matters enormously. A bug that confuses two account identifiers doesn't just cause a test failure — it can mean money going to the wrong place. The Haskell type system acts as a continuous, automated audit of these invariants.
Purity and Referential Transparency
Haskell's emphasis on pure functions (functions with no side effects) makes reasoning about code behavior significantly easier. When you're debugging a transaction processing bug at 2 AM, knowing that a function cannot have hidden state mutations is genuinely valuable.
This also improves testability. Pure functions are trivially unit-testable without mocks or test databases.
Concurrency Without the Terror
Haskell's runtime has mature support for lightweight concurrency through Software Transactional Memory (STM) and green threads. For a banking backend handling thousands of concurrent requests, this provides both performance and correctness guarantees that are harder to achieve in languages with mutable shared state.
The Real Challenges: Honest Tradeoffs
Mercury's engineers have been refreshingly candid about the downsides. Here's an honest assessment:
Build Times at Scale
Haskell compilation is notoriously slow. At two million lines, incremental build times can stretch into minutes even with modern tooling. Mercury has invested heavily in:
- Remote build caching to avoid redundant recompilation
- Nix-based reproducible builds for consistent environments
- Module boundary design to minimize recompilation cascades
Tools like Cachix — a binary cache for Nix — have become practically essential for teams of this size. It's not cheap at scale, but the developer experience improvement is measurable.
Hiring and Onboarding
The Haskell talent pool is smaller than Python, Go, or TypeScript. Mercury has addressed this in a few ways:
- Hiring for aptitude over Haskell experience — many Mercury engineers learned Haskell on the job
- Investing in internal education — structured learning paths, pairing programs, and documentation
- Building strong internal abstractions that shield newer engineers from the most advanced type-level programming
This is a genuine tradeoff. Hiring takes longer, and ramp-up time is real. But Mercury's argument is that the engineers who self-select for a Haskell shop tend to be unusually strong, and the type system catches enough bugs to justify the investment.
Tooling Maturity
Compared to the JavaScript or Python ecosystems, Haskell's tooling is less mature. IDE support via HLS (Haskell Language Server) has improved dramatically over the past few years, but it still lags behind what TypeScript developers take for granted.
Mercury's team has contributed back to the ecosystem in meaningful ways — a good example of how large production users can improve open-source tooling for everyone.
[INTERNAL_LINK: functional programming in production]
Production Engineering Practices at This Scale
Monorepo Architecture
Mercury operates a monorepo — a single repository containing the majority of their Haskell code. This has significant implications:
Pros:
- Atomic cross-service changes
- Shared library code without versioning headaches
- Unified CI/CD pipeline
- Easier large-scale refactoring
Cons:
- CI times grow with the repo
- Requires investment in smart build systems to avoid rebuilding everything on every change
For tooling, teams in similar positions often reach for Bazel or custom Nix setups. Mercury leans heavily on Nix, which provides hermetic, reproducible builds — critical when you want every engineer and CI server to be running identical environments.
Testing Strategy
At this scale, testing is not optional — it's structural. Mercury's testing approach reportedly includes:
- Property-based testing using Hedgehog or QuickCheck-style libraries — particularly valuable for financial logic where edge cases are numerous
- Integration tests against real database schemas
- Contract testing between services
- Extensive use of types as tests — encoding invariants in the type system so the compiler enforces them at build time
Property-based testing deserves special mention here. Instead of writing individual test cases, you describe properties that should hold for all valid inputs, and the framework generates hundreds of test cases automatically. For financial calculations, this is extraordinarily powerful.
[INTERNAL_LINK: property-based testing Haskell]
Deployment and Infrastructure
Mercury runs on AWS, using a fairly conventional cloud-native infrastructure despite the unconventional language choice. Haskell compiles to native binaries, which means:
- Docker images are small (no runtime interpreter needed)
- Cold start times are fast
- Memory usage is predictable
This is actually an underappreciated advantage of compiled functional languages. Your AWS ECS or Kubernetes pods start faster and use less memory than equivalent Python or Node.js services.
Lessons Other Engineering Teams Can Apply
Even if you're not building a Haskell codebase, Mercury's production engineering story contains broadly applicable lessons:
1. Invest in Your Type System
Whatever language you use, push it toward more type safety. TypeScript's strict mode, Rust's ownership model, even Python's type annotations with mypy — more type information means more bugs caught before production.
2. Make Illegal States Unrepresentable
This is a Haskell mantra, but it applies everywhere. Design your data models so that invalid states can't be constructed. This reduces defensive programming and makes code easier to reason about.
3. Build Times Are Developer Experience
If your build takes 15 minutes, developers will avoid running it. Invest in caching, incremental compilation, and parallelism. The ROI on developer experience compounds over time.
4. Hire for Learning Ability, Not Current Stack
Mercury's willingness to hire strong engineers and teach them Haskell has worked. If you have a differentiated technical approach, don't limit yourself to people who already know it.
5. Contribute Back to Your Ecosystem
Mercury's engineers have contributed to Haskell tooling, libraries, and community resources. This isn't just altruism — it improves the tools they use daily and helps with recruiting.
[INTERNAL_LINK: engineering culture fintech]
Is Haskell Right for Your Team?
Let's be honest: probably not, unless you have specific reasons to choose it.
Haskell is a powerful tool with real advantages in domains where correctness is critical and the team has the expertise to use it well. But the hiring challenges, tooling gaps, and learning curve are real costs.
Haskell might make sense if:
- Correctness is your primary concern (financial, medical, safety-critical software)
- You have existing Haskell expertise on the team
- You're willing to invest in hiring and onboarding
- You value long-term maintainability over short-term velocity
Consider alternatives if:
- You need to hire quickly
- Your team is already productive in another language
- Your domain doesn't have extreme correctness requirements
For teams who want Haskell's benefits without the full commitment, consider:
- Rust for systems programming with strong types
- TypeScript (strict mode) for web services
- Scala or F# for functional programming with larger talent pools
- Elm for frontend functional programming
Key Takeaways
- Mercury has built one of the largest known Haskell production codebases — a couple million lines — for their fintech banking platform
- The choice was deliberate: strong types and pure functions reduce bugs in financial software where correctness is critical
- Real challenges include slow compilation, smaller hiring pool, and less mature tooling — all of which Mercury has addressed with specific engineering investments
- Their practices (monorepo, Nix builds, property-based testing, remote caching) offer a blueprint for any team operating a large functional codebase
- The lessons generalize: invest in type safety, make illegal states unrepresentable, and treat build performance as a developer experience problem
- Haskell isn't right for everyone, but Mercury's success demonstrates it can work at serious production scale with the right team and investment
Final Thoughts and CTA
Mercury's story with a couple million lines of Haskell in production is one of the most compelling case studies in unconventional engineering choices paying off. It's not a story about Haskell being magic — it's a story about a team making a deliberate technical bet, investing in the infrastructure to support it, and being honest about the tradeoffs.
Whether you take away "we should try Haskell" or "we should apply these correctness principles in our existing stack," there's actionable value here.
Want to go deeper?
- Check out Mercury's engineering blog for first-hand accounts from their team
- Explore Haskell.org for learning resources
- Try Monday Morning Haskell for practical, production-focused Haskell tutorials
- Join the Haskell Discourse community to connect with practitioners
Have you worked with Haskell in production, or are you considering it? Drop your experience in the comments — real-world data points help everyone make better decisions.
Frequently Asked Questions
Q1: Is Mercury's Haskell codebase really a couple million lines?
Yes. Mercury engineers have publicly discussed the scale of their codebase in conference talks and blog posts. While exact numbers vary by what's counted (tests, generated code, etc.), the core backend is genuinely in the millions of lines of Haskell — making it one of the largest known production deployments of the language.
Q2: How does Mercury handle Haskell's slow compilation at scale?
Mercury uses a combination of remote build caching (via Nix and tools like Cachix), careful module boundary design to minimize recompilation cascades, and CI infrastructure optimized for parallel builds. It's an ongoing engineering investment, not a solved problem.
Q3: Can a team learn Haskell on the job, or do you need to hire experts?
Mercury's experience suggests you can hire strong engineers and teach them Haskell, but it requires structured investment: documentation, pairing programs, internal learning resources, and patience with ramp-up time. It's not a strategy for teams that need to move fast immediately.
Q4: What are the main benefits of Haskell for financial software specifically?
The primary benefits are correctness guarantees through the type system (preventing category errors between monetary values, account IDs, etc.), pure functions that make reasoning and testing easier, and mature concurrency primitives for handling high-throughput transaction processing safely.
Q5: Are there companies besides Mercury using Haskell in production at scale?
Yes, though Mercury is among the largest. Other notable examples include Standard Chartered (financial), GitHub (some internal tooling), Facebook (the Sigma spam detection system was written in Haskell), and various smaller fintech and blockchain companies. The production Haskell community is small but active and growing.
Top comments (0)