<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ketis Dev</title>
    <description>The latest articles on DEV Community by Ketis Dev (@ketisdev).</description>
    <link>https://dev.to/ketisdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3923348%2F46cd1140-a204-4511-bff8-a5d3d44f6b21.png</url>
      <title>DEV Community: Ketis Dev</title>
      <link>https://dev.to/ketisdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ketisdev"/>
    <language>en</language>
    <item>
      <title>Building an AI-Powered Driving Theory Exam Platform with the FSRS Algorithm</title>
      <dc:creator>Ketis Dev</dc:creator>
      <pubDate>Sun, 10 May 2026 14:04:57 +0000</pubDate>
      <link>https://dev.to/ketisdev/building-an-ai-powered-driving-theory-exam-platform-with-the-fsrs-algorithm-2nei</link>
      <guid>https://dev.to/ketisdev/building-an-ai-powered-driving-theory-exam-platform-with-the-fsrs-algorithm-2nei</guid>
      <description>&lt;p&gt;When we set out to build &lt;a href="https://ketis.lt" rel="noopener noreferrer"&gt;KETis&lt;/a&gt; — an AI-powered platform that helps Lithuanians prepare for their driving theory exam (KET) — we faced a deceptively simple question: how do you teach 2,500+ exam questions across 17 license categories without burning students out?&lt;/p&gt;

&lt;p&gt;The answer turned out to be an algorithm called &lt;strong&gt;FSRS&lt;/strong&gt; (Free Spaced Repetition Scheduler). In this post, I'll walk through why we chose it over the classic SM-2 approach, what surprised us during implementation, and what 82% pass rates taught us about adaptive learning at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Traditional Study Apps
&lt;/h2&gt;

&lt;p&gt;Most exam prep platforms — and almost every driving school app in Lithuania — use one of two approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static question pools.&lt;/strong&gt; Random questions, no memory of what the user knows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linear progress.&lt;/strong&gt; Question 1 → Question 2 → ... → Question 2,500. Brutal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both ignore a basic truth from cognitive science: &lt;strong&gt;the optimal moment to review something is right before you forget it.&lt;/strong&gt; Review too early and you waste time. Review too late and you're effectively re-learning from scratch.&lt;/p&gt;

&lt;p&gt;This is the core insight behind spaced repetition.&lt;/p&gt;

&lt;h2&gt;
  
  
  SM-2 vs FSRS: Why We Switched
&lt;/h2&gt;

&lt;p&gt;The most famous spaced repetition algorithm is &lt;strong&gt;SM-2&lt;/strong&gt;, popularized by SuperMemo and Anki in the 1980s. It works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;repetitions&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;repetitions&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;previous_interval&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;EF&lt;/span&gt;
    &lt;span class="n"&gt;EF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EF&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;repetitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's elegant. It works. But it has problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hard-coded constants&lt;/strong&gt; were tuned by hand decades ago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No personalization.&lt;/strong&gt; It treats every user the same.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary failure mode.&lt;/strong&gt; One wrong answer resets your progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FSRS, developed by Jarrett Ye starting in 2022, is fundamentally different. It models memory as a &lt;strong&gt;three-component system&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty (D)&lt;/strong&gt; — how hard a card is for &lt;em&gt;this&lt;/em&gt; user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stability (S)&lt;/strong&gt; — how long the memory will last before recall probability drops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrievability (R)&lt;/strong&gt; — the current probability the user can recall the item.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The algorithm uses ~17 parameters that are fitted per-user from review history using gradient descent. The output is a recall probability curve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;R(t) = exp(ln(0.9) * t / S)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;t&lt;/code&gt; is days since last review and &lt;code&gt;S&lt;/code&gt; is the current stability. We schedule the next review when &lt;code&gt;R&lt;/code&gt; drops to a target retention (we use 0.9).&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in Production
&lt;/h2&gt;

&lt;p&gt;On &lt;a href="https://ketis.lt" rel="noopener noreferrer"&gt;KETis&lt;/a&gt;, every time a student answers a question, we update three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The card's stability &lt;code&gt;S&lt;/code&gt; based on whether they answered correctly.&lt;/li&gt;
&lt;li&gt;The card's difficulty &lt;code&gt;D&lt;/code&gt; (smoothed exponential moving average).&lt;/li&gt;
&lt;li&gt;The user-specific parameter set, retrained nightly on accumulated review data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a queue that adapts to the individual. A student who breezes through right-of-way questions but struggles with priority signs will see priority signs more often — but not so often that they burn out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Surprising Implementation Lessons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cold start is real
&lt;/h3&gt;

&lt;p&gt;FSRS needs ~50 reviews per user before its parameter estimates stabilize. For a brand-new user, you're essentially running on default parameters. We solved this by seeding new accounts with a &lt;em&gt;category-level&lt;/em&gt; parameter set derived from the population, then gradually weighting toward the user's own history.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. "Learning" ≠ "Reviewing"
&lt;/h3&gt;

&lt;p&gt;FSRS is a &lt;em&gt;review&lt;/em&gt; scheduler. It assumes the user has already encountered the material. For brand-new content, you need a separate "learning" phase with shorter intervals (we use 1m, 10m, 1d). Mixing the two states in one queue was our first big bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Database design matters more than the algorithm
&lt;/h3&gt;

&lt;p&gt;We store every review event, not just the latest state. Storage is cheap; the ability to retrain parameters when the algorithm improves is priceless. We learned this the hard way after FSRS-4 → FSRS-5 came out and we had to re-derive parameters for every user.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. UX trumps optimality
&lt;/h3&gt;

&lt;p&gt;The "theoretically optimal" review schedule sometimes wants users to wait 47 days before seeing a card again. Users hate this — it feels like the app forgot about them. We cap intervals at 14 days for the first 3 months of use, then gradually let the algorithm breathe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;After 18 months in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;82% first-attempt pass rate&lt;/strong&gt; on the official KET exam (national average is around 60%).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Median study time to exam-ready: 11 days.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2,500+ questions across 17 license categories&lt;/strong&gt;, all scheduled per-user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're not claiming FSRS is magic. The platform also does adaptive question generation, mistake-pattern analysis, and weak-topic surfacing. But FSRS is the backbone — the thing that keeps the queue from becoming overwhelming or boring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should You Use FSRS?
&lt;/h2&gt;

&lt;p&gt;If you're building any kind of repetitive learning product — language apps, exam prep, professional certification, medical board prep — yes. The open-source implementations are mature (there's a Python library, a Rust port, and a JavaScript version). The main cost is engineering discipline around event logging and parameter retraining.&lt;/p&gt;

&lt;p&gt;If you're building a one-off course or content where users encounter material once and move on, stick with traditional sequencing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/open-spaced-repetition/fsrs4anki" rel="noopener noreferrer"&gt;FSRS algorithm paper and source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ketis.lt" rel="noopener noreferrer"&gt;KETis&lt;/a&gt; — our production deployment for Lithuanian driving theory exams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've shipped spaced repetition in production, I'd love to hear what tripped you up. Drop a comment below — especially if you found a better way to handle the cold-start problem.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>algorithms</category>
      <category>learning</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
