Case study

10 words

A mobile vocabulary app built around one small, finishable habit: ten new words a day, a short daily quiz and a weekly exam built only from the days you completed.

I was responsible for the full build of the product: architecture, backend, mobile app, and deployment.

  • React Native + Expo
  • .NET 8 ASP.NET Core
  • EF Core + SQL Server
  • Firebase Auth
  • Google Cloud Run
10 words login screen on a phone
Product
Mobile vocabulary trainer for iOS and Android
Role
Everything: design, API, app, infrastructure
Language pairs
EN→PL · ES→PL · ES→EN · FR→EN
Live
10-words.app ↗

The app in action

Drawing today's 10 words, plus a word detail card with phonetics and an example sentence
Draw your 10. Skip the words you already know, then start learning.
Review center with today's review and the locked weekly exam
Review center
Multiple-choice question in today's review
Multiple choice
Complete-the-sentence typing question with a hint
Typing
iPhone home screen with the 10 words widget showing five words and their translations
Home-screen widget. Today's words rotate every two hours, even when the app is closed.

Language apps ask for too much

Endless courses and long lessons work until one busy day breaks the habit, and then the app gets abandoned.

  • Sessions are too big to finish. A half-hour lesson loses to everything else in the day.
  • Exams test words you never saw. Random question banks have nothing to do with what was actually studied.
  • Reminders ignore the learner's day. A fixed server hour is wrong as soon as the user is in another time zone.

One small pack, one clear loop

The number is the product: 10 words a day, 3,650 a year.

  1. 1Morning pushAt your hour, in your time zone
  2. 2Draw 10Skip words you know
  3. 3FlashcardsTerm, translation, example
  4. 4Daily quiz5 choice + 5 typing
  5. 5100% = streakCounts toward Sunday
  6. 6Weekly examAfter 5 perfect lessons

Smart daily pack

Words banded by CEFR level, with a placement test to set the starting point.

Earned weekly exam

20 questions drawn only from the lessons you aced that week, in a 24-hour window.

Reminders at your hour

Daily, exam, streak-at-risk and birthday pushes, localized PL/EN.

Widget and offline mode

An iOS widget, plus cached screens that stay readable without a connection.

Scope of responsibility

Designed

  • the rules of the daily loop and exam gating
  • the data model and the REST contract

Built

  • the .NET 8 API with background jobs
  • the Expo app, widget and landing page

Delivered

  • a Dockerized API on Google Cloud Run
  • the landing site on Firebase Hosting

Architecture

Clients talk to one REST API. The API owns every rule and reaches the database and external services.

What was actually hard to get right

01

A weekly exam that follows the learner's clock

Problem. The exam must open on Sunday at each learner's own reminder hour, stay open for 24 hours and use only days they aced.

Solution. One service resolves the whole exam state per user, with the window computed from their time zone. The qualifying lessons are snapshotted when the exam starts, so later activity can't change its questions.

02

Drawing ten words that fit and never repeat

Problem. A pack has to match the learner's level and avoid every word already quizzed or skipped, even across redraws and half-finished days.

Solution. Selection excludes used and discarded words, fills the learner's level band first, blends in a couple of words from the next band and widens the range if the catalog runs thin.

03

What "today" means for every user

Problem. Packs, streaks, push hours and exam windows all depend on "the day", which differs per user and shifts with daylight saving.

Solution. Each user has a validated IANA time zone synced from the device, and all date logic goes through a single service. Streaks are evaluated lazily on read, so no midnight job is needed.

04

Push notifications without a queue

Problem. Send the right message at each user's local hour, never twice, and never let one bad account break the batch.

Solution. A hosted service polls every 15 minutes and skips anything already logged for that user, day and kind. A log row is written only for delivered pushes, so failures retry on the next tick.

Technical scope, in numbers

46REST endpoints
22,665words in 4 language pairs
4kinds of push notification
2background jobs
97localized API error codes

Technologies

Backend

  • ASP.NET Core (.NET 8)
  • Entity Framework Core
  • SQL Server
  • FluentValidation
  • AutoMapper
  • EPPlus
  • MailKit

Mobile

  • React Native
  • Expo Router
  • expo-widgets
  • expo-notifications
  • i18next

Auth and infrastructure

  • Firebase Auth
  • Expo Push
  • Google Cloud Run
  • Firebase Hosting
  • Docker
  • EAS Build