Recurse · learning game · TypeScript · Rust
Duolingo for computer science, checked by the compiler
Recurse teaches computer science one short lesson a day, as a game: XP, streaks, leagues, quests, badges and friends, on Duolingo's loop of small steps and instant feedback. Underneath, every claim about code has been proved by the real Rust compiler before anyone sees it.
Role
Sole author
Learners
100+ active
Courses
Rust, OS, system design, DDIA
Stack
Next.js, Hono, Drizzle
A lesson a day, played like a game
Each lesson is about five minutes and every answer is a tap, so it fits on a phone between other things. You pick a course, follow a path of units on a map, and Loop, the mascot, keeps you company on the way.
Four courses so far: Rust, operating systems, system design, and a course that follows Designing Data-Intensive Applications chapter by chapter.
Learning
Feature
What it does
Teach before asking
Interactive teaching cards and a short guide open each unit, so a question never tests something you have not been shown.
Eight question types
Will it compile, predict the output, find the bug, fill the gap, put the lines in order, match the pairs and more, all answered by tapping.
Real code
Every Rust example is compiled by rustc before it ships, so the answer key is always right.
Review that comes back
Skills you got wrong come back on a spaced schedule, in practice sessions built from your weak spots.
Notes for the long reads
The DDIA and system design courses add short notes with step-through diagrams, guess-first questions and small playgrounds.
The game
Feature
What it does
XP and combos
10 XP a lesson, 5 more for a perfect one, and a bonus for every 5 right in a row.
Streaks
One lesson a day keeps it going. Freezes cover a missed day, a missed streak can be repaired the next day with two lessons, and milestones get celebrated.
Weekly leagues
Groups of 30 race on XP each week. The top 7 move up and the bottom 5 move down, through Byte, Stack, Heap, Kernel and Compiler.
Daily quests
Three small goals a day, like a perfect lesson or 5 right in a row. Finish them all for 15 minutes of double XP and a streak freeze.
Badges
15 to earn, from Hello, world and Clean compile to Hundred days and One whole year.
Surprise XP
Now and then a lesson pays out a little extra, so finishing one always has something to look forward to.
Friends and habits
Feature
What it does
Friends
Follow people, open anyone's profile from a league, cheer them on, and see a feed of what friends finished.
Invite to unlock
Some sections open with an invite, and both friends get them when one joins.
Daily goal and reminders
Pick a daily XP goal and a time when you sign up. Reminders come as push or email, plus a weekly summary.
Share cards
A streak or a finished unit becomes an image ready to post.
Play first, sign up later
Start as a guest. Signing in with GitHub, Google or an email link keeps everything you earned.
Installs like an app
It is a PWA, so it sits on a phone's home screen and opens full screen.
A wrong answer key teaches the wrong thing
Most questions in a Rust lesson are claims about code. “Does this compile?” “What does this print?” If the answer key is wrong, the learner who got it right is told they are wrong, and the one who got it wrong walks away sure of something false. On a course of a couple of thousand questions, a few will be wrong if a person is the only check.
The second problem is the usual one for anything with points. Feedback has to be instant, so the browser needs to know the answers. But if the browser grades and the server believes it, XP, streaks and review timing are whatever anyone sends.
Two rules came out of that. The compiler, not the author, decides what is true about code. And the server grades every lesson again before anything counts.
How I got there
Three passes, in the order they happened.
Explore
Trust a person to get it right
Realise
Every one of those can be confidently wrong
Insight
Make the toolchain the answer key
Do not trust the author. Ask rustc.
From a line of YAML to a counted answer
Content is files in git, not rows in a database. It is checked, bundled and versioned at build time, and graded twice at run time.
Write the item
Each unit is a folder of YAML: the skills it teaches and what they depend on, the questions, and the teaching cards. There are eight question types, all answered by tapping.
content/rust/units/
Compile every one
A checker turns each item into one or more real Rust programs and builds them with rustc, pinned to one version and the 2024 edition. Teaching cards are compiled too. The checks run in parallel, each in its own temp folder.
tools/check-content/src/rustc.ts
Fail for the right reason
A “will it compile” item that says no must fail with exactly the error code it names, and nothing else. Failing for some other reason is a failed check. A “predict the output” item is run with a five second limit and its output compared to the answer.
Bundle and version it
Checked content becomes a JSON bundle with a version. A lesson started against an older version gets a 409 and the app reloads, so nobody is graded against questions that have changed under them.
Grade in the browser, instantly
The browser has the answers and grades on tap. That is what makes it feel like Duolingo rather than a form.
Grade again on the server
When the lesson ends, the server plays it again from the start with the learner's answers and grades every one itself. Only then do XP, streaks and review times change.
packages/api/src/replay.ts
An item, as it is written
The author states the answer. The checker compiles the code and fails the build unless rustc reports exactly E0308.
- type: will_it_compile
level: 2
teaches: [rust.traits.generic_fn]
prompt: Does this compile?
rust_mode: file
code: |
fn first<T>(a: T, _b: T) -> T {
a
}
fn main() {
let x = first(1, "two");
}
compiles: false
error_code: E0308Lesson rules with no database and no UI
Grading, building a lesson, scoring and session state live in one package of plain TypeScript that knows nothing about React or Postgres. The browser and the server run the same code, which is the only reason grading twice can agree. Anything random in it is seeded, so a replay draws the same lesson.
Because it has no dependencies to fake, it is also the easiest part to test, and most of the 41 engine tests are rules a learner would notice if they broke.
Rule
Why
12 items a lesson, at most 4 of one type
Enough to practise, short enough for a bus stop, and never a wall of the same question shape. Lessons that introduce new skills are 8.
Ordering is judged against the nearest valid answer
Some code has more than one correct line order. A learner's order is compared with whichever accepted order is closest to it, so a valid answer is never marked wrong.
Review is scheduled per skill, with FSRS
A maintained spaced repetition algorithm rather than a hand-rolled copy of Duolingo's. Only first tries count.
10 XP, +5 for a perfect lesson, +2 for every 5 in a row
Small and predictable. The server computes it from its own grading, never from what the browser says.
Guests can play before signing up
Progress moves to the new account in one transaction when a guest links GitHub, Google or an email link. Days both had activity are added together, not overwritten.
Where it stands
2,141
questions
86 units, 4 courses
100+
active learners
173
tests
151 unit, 22 end-to-end
Source · October 2026. End-to-end tests run in Chrome at phone and laptop size.
What CI refuses to merge
Every pull request runs lint, type checks, the tests against a real Postgres, the content checker with rustc, a production build, the end-to-end suite. A Rust question that no longer compiles the way it claims is a red build, the same as a broken test.
What Recurse came down to:
01
The compiler is the answer key
Every claim about Rust is proved by rustc in CI, down to the exact error code, before it can ship.
02
Fast in the browser, true on the server
Grading on tap feels instant; grading again on the server is what XP and review timing trust.
03
One engine, run twice
The same plain TypeScript rules run in both places, seeded, so the two gradings can only agree.