The problem
I'm learning German toward a hard deadline — A1 by the end of August 2026, B1 by the end of the year — while job-hunting for Program Manager and automation-enablement roles in the EU. Generic apps didn't fit that constraint in three specific ways:
- They gamify streaks, not mastery — there's no clean way to say "these 336 specific words are the ones I don't know yet, test me strictly on those."
- Their exercises don't mirror the actual Goethe-Institut exam format I'll eventually sit — no contextual Hören/Lesen/Schreiben/Sprechen structure, just isolated flashcards.
- None of them weave in workplace German — Gehalt, Vertrag, Besprechung — as a first-class track alongside everyday vocabulary, which is exactly the register I need for interviews.
So I scoped, built, and deployed a purpose-built tool instead of adapting my study plan to someone else's product.
What I built
A single-user web app tracking exactly where I stand on 732 tagged A1 vocabulary items (396 already known, 336 in active review), with:
- An SM-2 spaced-repetition engine where the "quality" input is derived automatically from quiz correctness and response speed, instead of a manual self-rating — one less decision for me to make mid-review.
- A skill-tree timeline from A1 to B2 where milestones unlock on live progress thresholds computed from the database, not the calendar.
- A Goethe-format quiz engine across all four skills — recognition and Artikel (der/die/das) drills for Wortschatz, curated Lesen/Hören comprehension passages, a Markdown Schreiben editor, and Web-Speech-driven Sprechen practice.
- A forgetting-curve dashboard surfacing which vocabulary clusters are decaying fastest, so review time goes where it's actually needed.
Technical stack
Product decisions worth explaining
The interesting part of this build wasn't the code — it was the tradeoffs. A few I'd defend in an interview:
This is a single-user tool. MySQL would have meant generating and safeguarding a second set of database credentials for zero real concurrency benefit. SQLite is one file, stored outside the web root so it's never directly downloadable, and it backs up by just copying that file.
My source word lists had no English glosses. Rather than spend a build cycle hand-translating 732 words, I redesigned the core quiz around recognition — hear or read the word, pick it out of same-category distractors. It turned a data gap into arguably better pedagogy: it tests active recall under a real constraint, not passive translation lookup.
Schreiben grading against Goethe rubrics needs an LLM. Rather than wire a paid API key in by default, the editor formats a grading prompt I copy into Claude's free chat and paste the feedback back in. One-click paid grading stays an opt-in toggle, not the default state of a personal project.
This page is meant to be seen by whoever's evaluating me, not locked behind a password. But the practice tools write to the same database I use for real study — so viewing and even trying the quizzes is open to everyone, while only my own login can actually update the progress numbers you're looking at.