All notes · Published 2026-10-22
How big is a TOEIC app, really? Handy 990 by the numbers: 650,000 lines of code, 28,640 questions, 80,000 audio clips
Handy 990 is exactly 156 days old, counting from the first day code was written. We went through the whole project and counted everything: code, questions, teaching material, pictures, audio, video, change history, releases, articles and design documents. This post lays all the numbers out, explains how each one was counted, takes a detour to explain what actually happens after you tap "Report a problem" inside the app — and tells you which numbers you should not take too seriously.
The short version: one table
| Item | Number |
|---|---|
| Days of development | 156 (first save on 2026-05-19) |
| Lines of code | 654,678 |
| Automated tests | 5,069 |
| Questions (seven TOEIC Parts + extra practice) | 28,640 |
| English in questions and material | about 1.16 million words |
| Translation strings | 976,479 |
| Pictures | 7,153 in the project + 2,538 paintings in the cloud |
| Audio clips | 79,854, about 80 hours |
| Videos | 24 |
| Code changes / reviewed merges / tickets | 5,523 / 1,521 / 1,226 |
| Releases | 81 (1.0.0 → 1.74.0) |
| Website pages / articles (before this post) | 445 / 84 |
| Design and engineering documents | 234 (57,536 lines) |
Now one item at a time.

Code: 650,000 lines, and the biggest file contains no logic at all
| Area | Files | Lines |
|---|---|---|
| The app itself | 301 | 297,228 |
| Automated tests | 303 | 133,087 |
| Web version | 426 | 116,584 |
| Scripts that generate, check and publish content | 336 | 89,262 |
| Cloud backend (forwarding reports, verifying subscriptions…) | 25 | 10,256 |
| Website generator | 8 | 7,568 |
| Small platform-specific pieces for each phone system | 5 | 693 |
| Total | 1,404 | 654,678 |
A few details worth knowing:
- The biggest file is the interface-text file: 20,712 lines, and not one of them is a line of "program". Every sentence the app ever shows you lives there: 2,960 pieces of interface text, 16,839 strings. Second place is the home screen at 17,232 lines. We know. We would love to split it too.
- Of the app's 297,000 lines, about 19,000 are tables generated automatically from data (the expansion table for the word-root lessons, the part-of-speech table for the answering rules). Those lines were not exactly "written", but they do have to compile and be tested.
- The app contains 3,945 program units, of which 1,550 are screen components and about 380 are complete screens. There are 98 kinds of data that sync across devices, each with its own merge rules. That is a post for another day.
- The 133,000 lines of tests are 45% the size of the app itself. There are 5,069 automated tests, and every change has to pass all of them before it goes into the official version. The web version has another 21,000 lines of tests.
By the way, "lines of code" is the worst productivity metric in the world. It measures not quality but how much there is to maintain. What 650,000 lines really means is: whatever you change, there are 650,000 lines you might break.

Questions: 28,640, in two piles
The first pile is TOEIC's own seven Parts: 11,032 questions in 4,364 question sets.
| Part | Questions |
|---|---|
| Part 1 Photographs | 335 |
| Part 2 Question-Response | 1,375 |
| Part 3 Conversations | 2,082 |
| Part 4 Talks | 1,536 |
| Part 5 Incomplete Sentences | 1,216 |
| Part 6 Text Completion | 680 |
| Part 7 Reading Comprehension | 3,808 |
The second pile is extra practice: 17,608 questions. Phrases 9,417, vocabulary 3,834, grammar 2,445, collocations 1,000, sentence reconstruction 441, error correction 246, accent identification 225.
A question is not finished when it is written. Every Part 1–7 question carries a difficulty tag and glosses: on each option for Parts 3–7, and on the transcript for Parts 1 and 2, whose options are heard rather than read; 8,329 of them (three quarters) carry answer evidence, the exact sentence in the passage or conversation that supports the key; Parts 5 and 6 also carry grammar tags; and 5,682 Part 5–7 questions carry a worked explanation. As for whether the bank is big enough, we counted that against 9,854 cloud backups in Is the question bank big enough? (in Chinese).
Teaching material: there is more of it than there are questions
A "question" is only the doorway to practice. The vocabulary, phrase and grammar modules each sit on layer after layer of data:
- 3,834 words, each with a dictionary sense, an etymology story, collocations (19,930 in all), pronunciation in three accents, and a read-aloud example sentence. Plus 737 word families, 201 sets of derived forms, 152 roots and affixes, 145 theme maps, and a 10,176-entry lookup dictionary you reach by long-pressing any word.
- 3,022 phrases, 9,417 questions, grouped into 196 phrase families; 149 phrases come with an origin story.
- 48 grammar topics, each with an explanation; plus a 46-chapter Grammar book with one comic per section, 139 comics in all.
- Everyday English: 290 English proverbs (12 themes), 230 tricky-pronunciation words (8 kinds of trap), 480 cultural references (9 parts), and 136 absurd vocabulary stories (34 levels, four per level).
- The rest: 93 word-root lessons, 83 tip cards, 240 dictation sentences, 225 accent-comparison questions (each in three accents), a 77-level vocabulary playlist, 2,538 word paintings, and a vocabulary radio drama with 10 episodes out, currently gathering opinions (about 860 more are already recorded and waiting).
Add up all of that text and you get about 1.16 million English words in questions and transcripts (487,000 in reading, 673,000 in listening transcripts), plus 976,479 translation strings for the glosses. The content lives in 563 data files totalling 230 MB. At 250 words a page and 400 pages a book, that is roughly eleven and a half books.
Pictures, audio, video
Pictures: 7,153 in the project.
| Kind | Count |
|---|---|
| Word-meaning stickers | 3,672 |
| Store screenshots and promotional images | 1,029 |
| Website illustrations, screenshots and share cards | 565 |
| Cultural-reference illustrations | 484 |
| Listening Part 1 photographs and graphics | 357 |
| Grammar book comics | 139 |
| Vocabulary-story illustrations | 136 |
| Castles for the vocabulary campaign | 78 |
| Happysaur the mascot in various poses | 29 |
| Icons, branding, radio-cast portraits, other | 664 |
Another 2,538 word paintings sit in the cloud and download only when you open that word; they are not packed into the app.
Audio is the biggest block. The project holds 79,854 audio clips. We measured 250 of them at random and scaled up: about 80 hours in total, a bit over three days if you listened straight through without sleeping.
| Kind | Files | Estimated length |
|---|---|---|
| Listening question audio (Parts 1–4) | 9,306 | ≈ 21 hours |
| Vocabulary playlist (word, meaning, spelling, example, collocations) | 54,996 | ≈ 40 hours |
| Example sentences read aloud | 6,639 | ≈ 10 hours |
| Word pronunciation (US) | 3,834 | ≈ 1 hour |
| Phrases read aloud | 3,064 | ≈ 1 hour |
| Accent training | 1,365 | ≈ 1 hour |
| Warm-up questions, tricky pronunciation | 650 | < 1 hour |
That leaves out the 7,668 British- and Australian-accent word clips that live in the cloud and download on demand, plus 240 dictation sentences, 136 story narrations and 159 exam-room ambience tracks in other corners of the project. Of the playlist's 54,996 files, 3,834 spell a word out letter by letter. Nobody wanted to record those by mouth, so a computer did.

Video is the smallest: 24 videos on YouTube linked from the website (23 shorts and one long one), in two playlists. The project itself holds a single demo clip. The time that would have gone into video mostly went into everything above.
The development record: 5,523 changes, 1,521 reviewed merges, 81 releases
Three terms first. Every time code is saved as an official record, that is a commit (a change). A batch of changes has to be reviewed by someone else and pass the tests before it goes into the official version; that batch is a PR (a pull request, or merge request). And every bug, report and proposal is an issue (a ticket).
- 5,523 changes in 156 days: 35 a day on average, weekends included. July's 1,408 was the peak; every month since has landed between 1,250 and 1,400.
- 1,521 merge requests, 1,509 of them merged. Each one first has to pass all 5,069 automated tests. On 213 of them the discussion ran to five or more comments, which usually means the reviewer and the author went back and forth several rounds.
- 1,226 tickets, 1,190 closed. Of these, 562 are question reports and 534 are feedback. In other words, 1,096 tickets (89%) were opened by you, from the "Report a problem" button inside the app. This project's largest test team is unpaid. The remaining 130 we opened ourselves: feature proposals, bugs and to-dos. That pipeline deserves its own section, just below.
- 81 releases, from 1.0.0 on May 19 to today's 1.74.0: one every 1.9 days on average. That was never the plan; we only noticed looking back.
- We also sent 106 in-app announcements, including a few surveys. The latest results are in Users told us we were heading the wrong way (in Chinese).
What happens after you tap "Report a problem"?
You tap "Report a problem" on a question, type two sentences, send. Then this happens:
- Within seconds it becomes a ticket in our tracker, in the same queue as the 1,226 above. The things you did not have to fill in, the app attached for you: the question's ID, your app version, phone model, system version, and the language you use. These are exactly what fixing a bug needs most, and exactly what people writing an email almost always forget.
- Your phone remembers the ticket number. Under "Settings → My reports" you can see where it stands: received → in progress → fixed. If we cannot work out what you mean, the status becomes "waiting for your details", with a button for adding them. No account, no email, no logging in anywhere.
- Every reply we write on the ticket appears word for word on your phone. You can answer, attach a picture, or reopen the ticket after we close it and say "still broken".
- Once it is fixed, the ticket is labelled with the version that fixed it. Your side shows "Fixed in 1.xx", and the home screen shows "Your report has an update". You know it is time to update, and you know why.

That is why we keep asking, and asking again, for reports from inside the app rather than through other channels:
- Email: no question ID, no version, no phone model, so we have to write back with three questions first; and it is not in the tracker, so the next email easily buries it.
- Surveys: a survey is anonymous. We can see the problem but cannot find anyone to ask for details, and cannot tell you when it is fixed.
- Store reviews (the worst kind): we can post one public reply, but there is no conversation: no way to ask for the question ID or the version, and no way to tell you when it is fixed. Worse, a one-star review saying "question 1,204 has the wrong answer" stays on the store page for years after that question was fixed the next day.
Each of the 1,096 tickets filed from inside the app was read by a person, answered by a person, and followed by a notification once fixed. For reports sent anywhere else, we can only do our best.
The website: 445 pages, 84 articles
Before this post went up, the website, handy990.happysaur.com, had 445 pages, 102 of them in Traditional Chinese. Articles come in five sections, 84 in all: 24 technical documents, 33 language notes, 14 TOEIC guides, 3 field reports, and 10 dev notes. You are reading the eleventh.
Design documents: 234 of them, 57,000 lines
Nobody usually counts this one, but it probably says the most about how the project is made. Every feature gets a design document before any code is written: what problem it solves, why this way and not that way, how the data is stored, how it is tested, and what is deliberately left out. After the feature ships, the document stays, and the next person to touch it reads the document first.
| Category | Count |
|---|---|
| Feature designs | 81 (31,142 lines) |
| Content-writing standards and review | 24 |
| Engineering (sync, accounts, telemetry, media hosting, web version) | 18 |
| Marketing | 16 |
| Promotional copy | 57 |
| Business model | 4 |
| Store copy | 6 |
| Other | 28 |
Next to them sits a whole set of "tools for the tools": 336 scripts, 51 one-command build targets, and 11 packaged workflows (writing questions, reviewing them, cutting a release, fixing a bug, writing posts like this one…). Not one line of these ever appears in the app, but without them none of the content above could be made.
A few numbers that fit nowhere else
- The project holds 117,208 files, 5.06 GB; with the full history of every change, 8.7 GB.
- Another 2.4 GB of half-finished content has not passed review yet and is not in the app.
- 32 extra-practice modules (not counting proverbs, pronunciation, cultural references and stories, the "Everyday English" group).
- As of September 8, 9,854 cloud backups.
How does a tiny team of seven pull this off?
Handy 990 is made by seven people, in 156 days. (Seven is this site's long-standing official figure. If the app's "About the Team" page says "independently built and maintained", please pretend you did not see it; and if you count the people mentioned below and get one, that is because the other six are very discreet.) Honestly, there is no secret to the method, only a few rules set on day one and never broken since:
- Write the document first, then the code. Those 81 feature designs were not written afterwards. A feature whose "why" and "what we will not do" are unclear does not get started.
- All content goes down the same production line. Every question passes through: write → check against the existing bank for duplicates → human review → publish. A Part 7 passage and the translation of a single proverb get the same treatment. Tools (AI tools among them) help with first drafts and with finding problems, but not one question goes live without a person having looked at it.
- Tests stand at the door. 5,069 automated tests, and every batch of changes must pass all of them. That is what lets us dare to ship every 1.9 days.
- The people using it tell us what is broken. 89% of the tickets come from the "Report a problem" button in the app. We answer every one, and once it is fixed we tell the reporter inside the app.

Seven people (let's say), 156 days, 650,000 lines: it sounds fast. But the time never goes into "making it". It goes into "deciding, once it is made, whether it is right". Documents and tests are how those decisions get kept.
But honestly
As always, let's be clear about what this does not mean:
- Big is not good. 650,000 lines, 28,000 questions, 80 hours of audio mean nothing for your practice tomorrow. What matters is whether the ten questions pushed to you today were the right ten. These numbers describe "how much there is to look after", not "how good it is".
- The audio hours are a sampled estimate, possibly off by a few hours. The word counts split on spaces, not on words in the linguist's sense.
- The line counts include a great deal that is not program code: interface text, generated tables, tests. If you only want the lines of actual logic, the answer is roughly half the figure above.
- 562 question reports are not a medal. Each one is someone whose time was wasted by a faulty question. A big number means a big bank and many users, and it also means many fixes we owed.
- Every number is as of October 22, 2026. By next week it will be wrong.

This is one of Handy 990's dev notes. Related: Is the question bank big enough? looks at bank coverage through usage records, and Users told us we were heading the wrong way has the results of our latest survey (both in Chinese).