Mike Cann's avatar
Mike Cann
3 months ago

I made 100 programming languages talk to Convex

Over the course of about a week I spent 32 billion tokens, several stashed-up Codex resets, an entire Claude subscription and a small fleet of rented servers on one challenge. Could I get Convex, the realtime database I work on, to work in 100 programming languages?

And I'm happy to say yes, yes I could.

We have Fortran, a language from 1957, older than the internet itself, now doing realtime query subscriptions. COBOL. Actual x86-64 assembly. Verilog, which isn't even really a programming language, it's supposed to be used for designing chips, but it now also does Convex.

Let me just show you. On the right here is the Fortran client running inside a Docker container, subscribed to a counter in my Convex deployment, and on the left is the Convex dashboard for that deployment. Watch the terminal on the right as I run the increment mutation by hand in the dashboard:

0:00 / 0:00

Fortran saw it instantly. There's no refreshing or polling, it's a real WebSocket subscription pushing data down into Fortran. Every language runs the same little demo, which is a counter in a room, so demo:state reads the room's counter and demo:increment bumps it. Here's the loop that's watching it, straight from the example:

1call convex_live_start(client, 'demo:state', '{"room":"' // room // '"}', live, ok, error)
2if (.not. ok) error stop error
3
4do
5  call convex_live_next(live, value, ok, error, timeout_ms=-1)
6  if (.not. ok) error stop error
7  decoded = convex_count(value, count_ok)
8  if (count_ok) then
9    write (output_unit, '(A,I0)') 'live count: ', decoded
10  else
11    write (output_unit, '(A)') 'live value: ' // value
12  end if
13  flush (output_unit)
14end do
15

Super cool!

So in this post I'm going to take you on the journey, the rules I set down and sadly the languages that didn't make it. Along the way I'll be sprinkling in some history for the weirdest and most wonderful of them, and handing out some awards. Oh and I'll break down exactly what all of this cost, because quite honestly the numbers are kinda silly.

Everything is up on the site and GitHub if you want to poke around, so grab yourself a lovely cup of tea and let's take a look.

Fortran

Let's start with the grand daddy of them all, the oldest language on the list. The first Fortran compiler shipped in 1957, which is 12 years before the internet's daddy ARPANET sent its first message. It's so old it predates the word "software" being commonly used.

So you might be wondering, how does a language that old make calls across the internet to a cloud database? Well this is one of those languages that has to leverage a bit of C to make the low-level plumbing work. The client is about 2,000 lines of Fortran and about 1,000 lines of C.

I just love the thought that the people who designed Fortran were optimizing for punch cards, and that their language would one day speak TLS to a serverless realtime database. I mean... just... boom.

The rules

Now before we go any further, I know what you guys are like 😉 You're thinking "that's not all Fortran" or "that's not all of Convex, that's just the client". So let's quickly talk about the rules I set for my agents.

Rule number one: everything Convex-specific has to be written in the target language itself. The protocol, the request and response handling, the sync logic, all of it. I did allow ordinary plumbing libraries like libcurl, OpenSSL or a JSON parser for the low-level stuff, because that's what a normal client library would probably use anyway, and that's all that bit of C in the Fortran client is. Roughly two thirds of the languages didn't even need that much C.

Rule number two: this whole project is about the client side of Convex, not the server side. When I say a language is using Convex I mean it's talking to the backend, calling queries and mutations and subscribing to updates. The functions themselves are written in TypeScript as normal. So no, sadly there is no COBOL running on Convex's servers.

Rule number three is the big one: every client has to prove it works, to a bar I set in advance. When I say Convex "works" in a language that could mean it printed hello world, which doesn't really count, so the challenge is built around two badges. The yellow HTTP badge means the client can call queries, mutations and actions through the documented HTTP API. The green Live badge is the big one, as it means the client also holds a realtime query subscription open over a WebSocket, which is the part that makes Convex, well, Convex. Here's what each one asks for:

0:00 / 0:00

So how does a language actually earn one of those badges? It's certainly not by an AI agent saying "yep, done, looks great boss", because agents are, let's say, enthusiastic self-reporters.

Each language builds its client and example in a Docker container, then has to pass a 31-check test suite twice. Once against a local Convex backend, and once against a real hosted deployment over the real internet with real TLS. That second one really mattered, as a local backend is plain HTTP, so a client with a broken TLS setup passes everything locally and only falls over against the real thing. Four languages hit that, each in a different way. Every language on the site has its evidence, like this one for LOLCODE:

The evidence panel for LOLCODE on the challenge website: source commit, toolchain, image digest, protocol, and two lists of checks, "Conformance run" and "Hosted deployment", each showing 31 of 31 checks passedThe evidence panel for LOLCODE on the challenge website: source commit, toolchain, image digest, protocol, and two lists of checks, "Conformance run" and "Hosted deployment", each showing 31 of 31 checks passed

Oh and one last caveat. These are not fully featured client SDKs that are going to be officially supported by Convex or anything, so don't expect ongoing Fortran support from me. They cover what I consider the most important bits: queries, mutations, actions, JSON-safe values, auth forwarding and live subscriptions. So treat them as educational experiments, not production packages like the official TypeScript, Python or Rust ones. If you do need Convex in a language we don't have a client for, the docs show you how to generate one with OpenAPI.

COBOL

COBOL, the COmmon Business-Oriented Language, arrived in 1959 and was built for businesses, banks, insurance companies, governments and whatnot. The big idea, championed by the legendary Grace Hopper, was that code should read like English, so that even managers could read it. So instead of the symbols we're used to you would write things like:

1ADD INTEREST TO BALANCE GIVING NEW-BALANCE
2

That's real COBOL by the way, that's how you add two numbers together. And the crazy thing is COBOL never went away. A huge chunk of card transactions still touch a COBOL system somewhere on their journey through the banking system, and there are apparently over 200 billion lines of it still in production, which is just mental.

And now this ancient language also speaks Convex. In the official TypeScript client, calling a query is one line:

1await client.query("demo:state", { room });
2

In COBOL, it's this:

1MOVE "demo:state" TO WS-PATH
2MOVE 10 TO WS-PATH-LEN
3MOVE 1 TO WS-OP
4CALL "cvx-client-call" USING WS-OP WS-PATH WS-PATH-LEN
5    WS-ARGS WS-ARGS-LEN CVX-RESULT CVX-ERROR WS-STATUS
6IF WS-STATUS NOT = CVX-OK
7    MOVE "initial query" TO WS-STAGE
8    PERFORM FAIL-AND-STOP
9END-IF
10

First you move the function name into a variable. Then you move the length of the name into another variable because yes, COBOL makes you count the letters yourself. Then a 1 to say it's a query. Then you call the client using eight parameters, check the status and perform FAIL-AND-STOP if anything looks off.

So it is Convex, just a lot more verbose. It's nearly ten thousand lines including tests, which makes it one of the biggest clients in the whole project, and certainly one of the most shouty.

LOLCODE

To balance that out here's a joke language from 2007, invented at the height of the lolcat meme era (if you were around for that) and designed to read the way those cats talked. Every LOLCODE program has to open with HAI and end with KTHXBYE, and our Convex client literally begins like this:

1HAI 1.3
2
3CAN HAS TRANSPORT?
4CAN HAS STRING?
5

CAN HAS is LOLCODE's import statement by the way, so that's just pulling in the low-level networking module. Here's a bit from the example, which waits for live query updates:

1IM IN YR WAITLIVE
2  ...
3  I HAS A EVENT ITZ I IZ CONVEXLIVETICK MKAY
4  BOTH SAEM EVENT AN "", O RLY?
5    YA RLY
6      GTFO
7  OIC
8  ...
9

I HAS A EVENT is a variable declaration, so that's const event in TypeScript, and the ITZ after it is the equals sign. O RLY? is an if statement, YA RLY is the true branch and OIC closes it off. IM IN YR WAITLIVE is a loop, basically a while (true) with a name. And to break out of the loop... GTFO. Which I would argue is better than break, and I think we should adopt it in JavaScript.

This is 820 lines of real, working LOLCODE, and it passes the exact same 31 checks as every other language, twice. Fun fact: to make it work the agents had to patch the LOLCODE interpreter itself to wire in the low-level transport, which is just crazy.

How it went

Like most of my projects, this one started with me wildly underestimating it. The plan seemed simple. I would grab a list of the 100 most popular programming languages, then a coding agent would go through them one at a time, building the same example, running the same test suite and earning badges as it went.

And for the first stretch it honestly was that simple. Python, Go, Rust, Java, the agents banged those out really quickly, and even the slightly spicier ones like Haskell and Prolog fell without too much drama. But then we hit a bit of a wall.

The first problem was hardware. I was running everything on my laptop plus my two Mac Mini home servers, and it was taking a very long time and burning a lot of tokens. I kept getting segfaults and out of memory kills and just crazy stuff like that, and to cut a long story short it's because those machines are ARM and the test suite needed everything to run on Intel. Emulating that turned out to be slow and flaky. JavaScript, TypeScript, Erlang, Elixir, Clojure and Lua all failed under emulation and then passed first time on a real x86 machine.

So I did what any reasonable person would do and rented a fleet of servers, five x86 machines from Hetzner. The agents stayed on my Mac and the servers did all the heavy lifting over SSH, and things started moving much more quickly.

Then I ran into the second problem. I started this project on Codex and burned through every single one of the usage resets I had stashed up, and when those ran dry I swapped over to Claude and chewed through an entire subscription there as well. Let's just say the usage dashboards had a big month.

The third problem was the languages themselves. Once the first 60 or so are done, what's left is the really hard esoteric stuff, and a lot of those don't have great toolchains, so iterating on them was a bit of a slog for the agents. Case in point...

x86-64 assembly

In assembly there are no strings, no JSON and no WebSockets. Whatever you need you have to build yourself out of bytes and registers, so the agents hand-built the HTTP requests and the WebSocket frames byte by byte, only leaning on the C library for sockets and memory, and OpenSSL for the TLS. Just being able to read and write the JSON that Convex speaks took over 2,000 lines of assembly. Here's me scrolling through a bit of it:

0:00 / 0:00

Now I don't read assembly, so most of this is honestly just unintelligible to me. But I am told by my good buddy Claude that this part is where it picks the word "query" to send in its request:

1.op_query:
2    lea rax, [rel op_query]
3    mov [rbp-104], rax
4    mov qword [rbp-112], op_query_len
5    jmp .have_op
6...
7op_query: db "query"
8

So ye, close enough, I'm counting it.

The award ceremony

I could honestly spend all day going through each of these languages, but this post would get fairly unwieldy if I did. So instead, let me introduce you to the Convex 100 Language Challenge award ceremony.

First up, the best looking code award goes to... FreeBASIC. Ye, I know. I didn't expect that either. But go and have a look at it, it's clean, readable and in my opinion a thing of beauty. Maybe I should write all of my programs in BASIC from now on:

1' Increment once. runId is the idempotency key: because the room is unique to
2' this run, a retry after a lost response still counts exactly one increment.
3dim as JsonValue ptr incrementArgs = JsonNew(JSON_OBJECT)
4JsonSet(incrementArgs, "room", JsonNewString(room))
5JsonSet(incrementArgs, "language", JsonNewString("FreeBASIC"))
6JsonSet(incrementArgs, "runId", JsonNewString(room & "-once"))
7

At the polar opposite end of the spectrum, the most cursed language award goes to APL, from 1962, invented by the mathematician Kenneth Iverson. It actually started life as a mathematical notation for describing computation and only later became a programming language, and it's so symbol heavy that it needed its own keyboard just to type it. I'm not joking, here it is:

An old APL keyboard with cream coloured keys, each with extra mathematical symbols printed on itAn old APL keyboard with cream coloured keys, each with extra mathematical symbols printed on it

Even the comments start with a little lamp glyph. Here's a bit of its client. Sheer cursed madness if you ask me:

1⍝ Reads a full HTTP response (status line, headers, body) from CONN,
2⍝ honouring Content-Length or chunked transfer-encoding, bounded by an
3⍝ 8 MiB budget and a 15-second deadline. Returns ('k' (statusCode
4⍝ bodyChars)) or ('e' message).
5∇Z←HttpReadResponse CONN;BUF;DEADLINE;R;HEADEREND;HEADERTEXT;BODYSOFAR;STATUSLINE;RESTHEADERS;CONTENTLENGTH;CHUNKED;HEADERLINE;COLONPOS;HNAME;HVALUE;MAXBYTES;DECODED
6  MAXBYTES←8388608
7  DEADLINE←NowMs+15000
8  BUF←⍬
9  HEADEREND←0
10HDRLOOP:
11  →(HEADEREND>0)⍴HDRDONE
12  →(NowMs>DEADLINE)⍴HDRTIMEOUT
13  R←CONN ConnRecv (65536 5000)
14

Next up, the longest implementation award. In the video I gave this one to Ada at just over 11,000 lines, but it turns out I got that wrong 😅 According to the numbers it's x86-64 assembly at 11,347 lines, with Ada just behind on 11,106. Sorry Ada. To be fair most of Ada's lines are tests (about 7,100 of them), which is very Ada of it. The language is named after Ada Lovelace, arguably the first programmer in history and an absolute legend, and the US Department of Defense commissioned it for software that really must not fail, so think air traffic control, satellites and fighter jets.

On the other side of the scale, the shortest implementation award goes to Bash. Finally, a language you've probably heard of! It's the shell from 1989 that's probably sitting in your terminal right now, its name is literally a pun (the Bourne Again Shell) and it's the duct tape holding the entire software industry together at this point. Its three shell files come in at under 600 lines (not counting tests), with the HTTP part fitting in one 82 line file. So yes, if you've ever wanted to write Convex apps in Bash, now you can.

This next one is special. The award for the biggest nightmare to work with goes to Io, a tiny minimalist language from 2002. It fought the agents every single step of the way with segfaults, crashes and weird memory issues (at one point they had it running under valgrind to track down a double free), and it took five separate debugging sessions before it finally passed. It did get there in the end though.

And the final award of the evening, for "that's not even a programming language", goes to Verilog. It's from 1984 and chip designers use it to describe hardware circuits, so you don't really write programs with it. It runs in a simulator here rather than on an actual chip, but it still earned both badges and talks to a cloud database like it's no big deal.

The graveyard

Sadly not every language on my original list made it to the end. 25 of the slots on the list had to be swapped out for replacements along the way, and the graveyard page has what happened to each of them:

0:00 / 0:00

Some needed proprietary licenses or GUI-only tools, like MATLAB, LabVIEW and Scratch. Some couldn't open a network connection at all, like bc, the little Unix calculator language. And some were just stubborn. Raku needed more memory than our 128 MB container limit before it had loaded a single line of client code, and Mozart/Oz's old code generator kept crashing when the agents tried to add TLS.

The bill

Okay, the bit you've been waiting for. What did all of this actually cost?

Just over 32 billion tokens moved across this project, which is a lot, and it ended up split almost down the middle. Codex built the first 57 languages, then Claude (mostly lots of Sonnet sub-agents) carried the big pushes, and Codex came back at the end to do a lot of the cleanup. I also sprinkled on a little bit of Grok when I started getting worried about costs. If 32 billion is hard to wrap your head around, that's like feeding the entire English Wikipedia through these models five times over.

If you naively priced all of those tokens as fresh input at the API list prices from when I did this, you'd get about $110,000, which is a terrifying number but also quite misleading. You see 98% of those tokens were cache reads, which are billed at a tenth of the price. For Claude nearly 17 billion tokens moved, and only about 370,000 of those were ever read fresh (another 150 million or so got written to the cache). So if you price it with caching taken into account you get about $14,000:

Two charts from the challenge website. Tokens by provider: Claude 52.1%, Codex 47.7%, Grok 0.1%. What caching changed: about $472 actually paid, about $14,000 at cache-aware list prices, about $110,400 without cachingTwo charts from the challenge website. Tokens by provider: Claude 52.1%, Codex 47.7%, Grok 0.1%. What caching changed: about $472 actually paid, about $14,000 at cache-aware list prices, about $110,400 without caching

Now obviously I didn't actually spend $14,000 on this project. Convex did raise $57 million in its Series B, but I doubt I could have snuck a casual 14 grand past accounting.

What I actually used was a couple of Codex resets I had saved up and one entire Claude subscription cycle, and I'm a pretty heavy AI user so each of those are the $200 a month plans. Add about $100 on the Hetzner servers and a bit on Grok, and my estimate for the real out of pocket cost is around $500 (that $472 on the chart is from before the last of the server bill came in). And don't forget, I was probably going to spend those tokens on some project or other for my videos anyway.

So was it really worth 32 billion tokens for about 450,000 lines across 100 different programming languages? I think I'll let you be the judge of that. The full breakdown is in the repo if you want to check my working.

Conclusion

So other than being an epic flex, what are the takeaways here?

The first is that the reason 100 languages could do this at all, including ones from the 1950s, is that underneath the SDKs Convex speaks quite a small and simple protocol. It's just HTTP, JSON and WebSockets. So here's that same demo:state query from the COBOL example, but using curl. Any language that can make an HTTP request can do this:

1curl https://your-deployment.convex.cloud/api/query \
2  -H "Content-Type: application/json" \
3  -d '{"path": "demo:state", "args": {"room": "hello"}, "format": "json"}'
4
5{"status":"success","value":{"count":1.0,"lastLanguage":"curl","latestRunId":"one","room":"hello","updatedAt":1793668327277.0}}
6

The WebSocket side isn't a documented public API like the HTTP side is, so the agents had to work it out from the open source Rust and JavaScript clients and pin to a specific version of it. More than a dozen of them implemented the WebSocket protocol by hand over a raw socket, as their languages don't have a library for it. But basically, as long as a language can open a socket it can be a Convex client.

The second takeaway is about the size of the project. Before I started I thought this should be the kind of parallelizable task that AI should be able to do, but to be honest I wasn't sure. At its peak there were over 100 agents working together, each one coding a language, verifying that it worked or orchestrating it all, and it was a marvellous thing to watch. In one 24-hour period there were 26 languages, 489 commits and 80 pull requests merged, and I was asleep for a decent chunk of it.

My job through all of that was to be the director, choosing the goal, designing the rules and keeping the machine pointed in the right direction. It really opened my eyes, and I think this is how a lot of big software is eventually going to get written.

If you've got Docker you can try one of them yourself, just swap fortran for any language on the site:

1git clone https://github.com/mikecann/the-convex-100-language-challenge
2cd the-convex-100-language-challenge
3./run verify-example fortran
4

Until next time, cheerio! 👋

All gas, no breakages

Convex is the reactive backend platform that keeps up with you and your agents. Database, functions, workflow, sync, search, file storage, and more. All TypeScript, zero glue.

Get started