2026 - Present
Live, visit sitenihongojouzu.jp
A production JLPT study PWA with spaced repetition, push notifications, and a custom furigana engine, built solo, end to end.
Founder & sole engineer
- monthly infra cost, the .jp domain is the only bill
- ¥0
- spaced-repetition scheduler tuned for JLPT study
- SRS
- furigana rendering engine for ruby text
- Custom
- magic links + Google sign-in, no passwords stored
- Passwordless
The Problem
Studying for the JLPT N2 as a working engineer in Japan, I hit the same wall every learner does: flashcard apps treat Japanese like a list of English words. They can't render furigana properly, their review scheduling ignores how memory actually decays, and they don't survive a subway tunnel.
I wanted a study tool that behaves like a real product: reviews scheduled by a spaced-repetition algorithm, reminders that arrive as push notifications at the right moment, and typography that renders ruby text the way a Japanese textbook does.
I also wanted proof-of-craft: a system I own from database schema to service worker, running in production, with real users and real analytics driving iteration.
The Constraints
A budget of exactly ¥0
Built alongside a full-time senior engineering role, with a hard rule: no recurring infrastructure bills. Hosting, database, auth, push notifications, and analytics all had to fit on free tiers, and every architectural choice had to minimize operational load too.
Japanese typography is unforgiving
Furigana (ruby annotations above kanji) must align per-character across mixed kanji/kana text, at any font size, on any device. Off-the-shelf ruby rendering breaks on compound words and long readings.
Offline is the normal case
Study happens on trains. The app had to be installable and functional through connectivity gaps, which shapes state management, caching, and sync design from day one.
Auth without friction
Nobody wants a password for a study app. Sign-in is passwordless only (a magic link or Google) which means email deliverability, token expiry, OAuth redirects, and session handling all have to be right.
The Architecture
A serverless-first PWA assembled entirely from free-tier infrastructure: Next.js on the edge for the shell, Supabase for auth/data, the browser's own Web Push for notifications, and a client that treats the network as an enhancement. Review state lives in Zustand with persistent storage, so a study session never blocks on a request.
- The spaced-repetition scheduler computes the next review interval from answer quality and history, study sessions read and write locally first, then sync, so a tunnel never interrupts a review.
- The furigana engine tokenizes kanji/kana boundaries and emits per-segment ruby markup, solving alignment cases (compound words, long readings, mixed script) that naive <ruby> tagging gets wrong.
- Web Push review reminders run through the service worker, the app nudges you when reviews come due, which is the mechanic that makes spaced repetition actually work.
The Product



The Trade-offs
Every architecture is a set of bets. These are the ones I made, and what I gave up to make them.
Backend
I build APIs for a living; that's exactly why I didn't build one here. A solo side project's scarcest resource is maintenance attention. Supabase's auth, row-level security, and Postgres bought me a production backend for near-zero ops.
State management
For offline-first study flows, the client is the source of truth during a session. A small persisted store with explicit sync beats cache-invalidation gymnastics when the network is optional.
Furigana
I tried the libraries first. They mis-segment compound kanji and misalign readings, unacceptable in a product whose entire job is teaching correct readings. This was the one place where 'build' beat 'buy' on quality grounds.
Authentication
No stored passwords shrinks the security surface to nothing worth stealing. Google sign-in gives the one-tap path most users pick; magic links cover everyone else, and both flows share the same Supabase session handling.
Notifications
Web Push is a free, standard browser capability, no per-message pricing, no vendor SDK. It costs more engineering up front (subscription lifecycle, iOS install requirement), but it keeps the entire reminder system at ¥0/month forever.
Cold starts
Free-tier servers spin down when idle, so the first visitor eats a long cold start. An UptimeRobot health-check ping every 5 minutes keeps the instance warm, and doubles as free uptime alerting. The ¥0 bill stays ¥0, and no user ever waits for a cold boot.
AI in the workflow
Study content and code are generated and refined with Claude during development, then reviewed before anything ships. Production carries no LLM API key at all, nothing metered per request, and nothing to leak. I've seen what a leaked key costs in production; the cheapest key to secure is the one that doesn't exist.
The Outcome
nihongojouzu.jp is live, installable, and in active use, a complete production system owned end to end by one engineer.
- Shipped a full spaced-repetition study platform: scheduler, decks, review flows, streaks, and push-notification reminders.
- Shared free with the JLPT N2 community, no ads, no paywall. The ¥0 infrastructure is what makes giving it away sustainable.
- The entire production stack (hosting, database, auth, Web Push notifications, and analytics) runs on free tiers: ¥0/month in infrastructure, with the nihongojouzu.jp domain as the only paid line item.
- The custom furigana engine renders textbook-quality ruby text across devices, the feature users notice first.
- Instrumented with GA4 and Microsoft Clarity; session replays directly shaped the review-flow UX and onboarding.
- Serves as a living reference of my end-to-end range: product thinking, schema design, PWA engineering, auth, and analytics-driven iteration.
Lessons Learned
Offline-first changes everything upstream
You can't bolt offline onto an online app. Making the client the session source of truth dictated the state library, the sync model, and even the auth flow. Decide your connectivity story before your architecture.
Buy the platform, build the differentiator
Supabase for the commodity layers, custom code for the furigana engine, the inverse of what ego suggests and exactly what the product needed. Build only where you can be meaningfully better.
Analytics beats intuition, even at tiny scale
Clarity replays showed users hesitating exactly where I'd assumed the UX was obvious. Ten real sessions taught me more than a hundred self-tests. Instrument from day one, even with no users yet.