Skip to main content

The Next Version of Curling IO

· 7 min read
Dave Rapin
Dave Rapin
Founder @ Curling IO

The next version of Curling IO should feel familiar: the same registrations, schedules, scoring, and club admin workflows, just faster and harder to break. The work is mostly under the surface, but the payoff is practical: fewer slowdowns at peak registration, better reliability during live events, and a platform we can keep improving for a long time.

This series explains what changes, what doesn't, and why we're making the bet.

How This Affects You

For club managers: Curling IO isn't changing from your perspective. We're upgrading the system behind the scenes: no downtime, no disruption, no action required from you. Your data stays exactly where it is. When the upgrade is ready, you'll have the same Curling IO you know, with faster pages and fewer moving parts for us to operate.

For curlers: You won't notice the technology change. Registration, scoring, schedules, results: everything works the same way. You might notice pages feel snappier, especially during peak registration times or when checking live scores at major competitions.

Why a New Foundation?

Curling IO has been serving curling clubs since late 2014. We upgraded the platform once already in 2019, and our Version 2 system has been running successfully on Rails ever since. Everything works, clubs are happy, and growth is strong. So why invest in a new foundation?

Because we're planning to be here for the long haul. The current system works, but the next decade will ask more of it: more live scoring, more automation, more traffic spikes, and more ways for clubs to manage work without sitting in front of forms all night.

Three key reasons:

  1. AI Agent APIs - We're adding APIs designed for AI agents. Your existing web interface isn't changing. This gives clubs another way to handle structured work, like setting up leagues, checking registration numbers, or pulling reports.

  2. Concurrency and correctness - Peak registration and live scoring traffic can put real pressure on the system. We want a stack that handles those spikes cleanly and catches more mistakes before code reaches production.

  3. Easier developer onboarding - We want a codebase that doesn't require years of institutional memory before someone can safely change it. Type safety and functional patterns make the system easier for developers and AI coding tools to reason about.

Choosing the Tech Stack

Given those goals, we need a stack that can deliver:

What we're looking for

  • Type safety - Catch errors at compile time, not in production
  • High concurrency - Handle thousands of simultaneous registrations
  • Functional patterns - Predictable, testable code that developers can reason about
  • Shared types - Same data structures on client and server
  • Proven at scale - Used in production by major companies

Why Gleam?

Gleam fits what we need. It's a type-safe functional language that compiles to both Erlang (for the server) and JavaScript (for the client). You get:

  • BEAM VM reliability - The same runtime that powers WhatsApp and Discord. Built-in fault tolerance and lightweight processes.
  • ML-family type system - Stronger than Elixir, catches entire classes of bugs at compile time
  • Lustre for frontend - An Elm-architecture framework for Gleam that compiles to JavaScript. We already love Elm for our current frontend apps, and Lustre brings that same architecture everywhere. Same types from database to UI, all type-safe.
  • Simple language - Focused, consistent, easy to reason about. Perfect for building deterministic APIs.

What about other options?

We didn't jump straight to Gleam. We seriously considered several other stacks:

PostgREST + Elm/Lustre - This was a very serious contender using tools we're already familiar with. PostgREST provides instant APIs from your PostgreSQL schema, and Elm/Lustre provides type-safe UI. But we'd need something else for background jobs (like Go), and the tooling for building, debugging, and testing PostgreSQL functions isn't great. The multi-language setup felt fragmented.

F# SAFE Stack - F# is a strong functional language with a solid .NET ecosystem. The SAFE stack (Suave/Saturn, Azure/AWS, Fable, Elmish) offers full-stack type safety with The Elm Architecture (TEA) on the frontend. We liked this option, but chose Gleam for its BEAM VM benefits and simpler deployment story.

TypeScript + Node + React - The obvious choice given its large ecosystem and the amount of material AI agents can draw from. We've been using React off and on since 2014 and like Redux (which is Elm-inspired) for the frontend. But JavaScript/TypeScript has issues we couldn't get past: nulls everywhere, a type system that feels bolted on (TypeScript's safety is opt-out with any, as, and !), relentless library churn, and recurring npm supply chain vulnerabilities. Popularity is a real advantage, but it wasn't enough to outweigh the correctness and simplicity we wanted.

BEAM was the deciding factor. None of these other options gave us the same mix of concurrency, fault tolerance, and simple deployment. Gleam gives us BEAM on the backend with familiar functional patterns (like Elm) on the frontend, all in a single language.

Why not stick with Rails?

To be clear: we love Ruby on Rails. It's been amazing for rapid development and has scaled well for our needs. For heavier interactive features, we've already been using Elm for frontend apps like Bracket Builder, Scheduling, Scoring, and Results widgets, with PostgREST providing clean APIs on the backend. This functional programming approach has worked really well.

But for where we're headed, we need:

  • Compile-time safety (Ruby is dynamically typed)
  • BEAM-level concurrency (Rails doesn't even come close)
  • Shared client/server types (Rails is backend-only)
  • Functional patterns built-in (Rails is object-oriented)

This isn't about Rails being bad - we've been building with it since version 1.2 and nothing else lets you iterate that fast. But over time we've moved towards functional, compiled, typed languages because runtime errors are the absolute worst to debug and fix. Gleam gives us that correctness without giving up the developer experience we love about Rails.

SQLite Over PostgreSQL

This one surprised us.

Curling IO Version 2 runs on PostgreSQL. We assumed we'd use Postgres for Version 3. But we're betting on SQLite instead.

Why SQLite?

  • Isolated databases - Clean separation of concerns with dedicated database files
  • In-process speed - Zero network latency between app and database
  • Operational simplicity - No database server to manage, tune, or cluster
  • Cost savings - Orders of magnitude cheaper than managed Postgres
  • Litestream backups - Continuous replication to offsite storage

What about scale?

For the same infrastructure budget as Rails + PostgreSQL, our working target is:

  • 1,000x more concurrent connections
  • 100x the data volume
  • 100x the throughput during peak traffic (registrations, live scoring)

These numbers sound aggressive, so we're treating them as targets to prove, not marketing claims to coast on. BEAM processes are ~2KB each vs Rails threads at ~1MB, which is where the 1,000x connection multiplier comes from. Eliminating network round-trips to a separate database server also matters because SQLite runs in-process.

We'll validate the whole thing with benchmarking and real-world load tests as we build.

If SQLite doesn't scale as expected, we'll stick with PostgreSQL.

Vertical-First Scaling

Our scaling strategy is simple: start on a single powerful server and scale up from there.

  • One server with co-located app + database (one thing to deploy, backup, monitor)
  • Warm backup server + continuous offsite backups
  • Scale up CPU and RAM as we grow
  • No distributed systems complexity until actually needed

If we ever outgrow a single server, we have a clean path to splitting things up. But with BEAM's concurrency and SQLite's in-process speed, we expect that to be a long way off.

What's Next

We'll build this out alongside the existing platform. Curling IO Version 2 isn't going anywhere. It continues to run and receive updates as usual. When Version 3 is ready and thoroughly tested, we'll transition everyone over in one coordinated switch. We won't make that switch until we're confident everything is solid.


This is Part 1 of the Curling IO Foundation series. Next up: bilingual support with compile-time guarantees.