Полгода в одиночку строю Bookverse
Существует романтическая версия одиночной разработки софта, в которой у тебя чистый стол, эспрессо, и ты выпускаешь к обеду что-то, что люди обожают. Версия, которую я на самом деле прожил последние полгода, больше похожа на «четыре раза подряд переписать одну и ту же конфигурацию Nginx, потому что тебя только что укусил седьмой граничный случай.»
Это размышление — не чек-лист того, что выпущено, и не туториал. Просто заметки спустя полгода.
Почему я начал
Я годами с перерывами пользовался языковыми приложениями и заметил кое-что конкретное: я мог завершить 30-дневную серию в популярном геймифицированном приложении и при этом по-прежнему не суметь прочитать абзац текста на языке носителей. Петля обратной связи была плотной, геймификация — отточенной, но единицей прогресса было предложение, иногда слово. А чтение языков — настоящее чтение — происходит на уровне страниц.
Я хотел приложение, которое относится к учебнику как к учебнику. Открой главу первую. Прочитай её. Послушай её. Проговори её вслух. Изучи то, что осело. Завтра — глава вторая.
Форма продукта была ясна с первого дня. Подвох в том, что «форма ясна с первого дня» отвечает примерно за 5% разработки софта. Остальные 95% — это негламурная середина.
Негламурная середина
Неполный список того, что я сделал в этом квартале и чего нет ни в одном релизном примечании:
- Переименовал целое дерево путей монорепозитория из
app_v54_01вbookverse(и переименовал заново, когда захотел подчёркивания в имени юнита systemd). - Написал четыре версии конфигурации Terraform для vhost’ов Nginx, прежде чем
обнаружил, что триггер
null_resourceдолжен включать хеш отрендеренного шаблона, а не только входные переменные. - Потратил неделю на выяснение, почему значение переменной окружения
EXPO_PUBLIC_*резолвилось в пустую строку в веб-бандле, но прекрасно работало на iOS. (preset-expoот Babel пропускает инлайнинг дляnode_modules. В опубликованных пакетах нужны фабричные функции, а не переменные окружения.) - Отрефакторил 17 вызовов хелпера URL для API, потому что в первый раз централизовал его неправильно.
Ничего из этого не попало бы в маркетинговый пост о приложении. И всё это было настоящей работой.
О чём никто не предупреждает в одиночной разработке: нет никого, кто бы взял на себя скучную половину. Ты не можешь сказать «деплоями занимается команда платформы», потому что команда платформы — это ты. Ты неуклюже носишь каждую шляпу, пока не проносишь её достаточно долго, чтобы носить хорошо.
Кроличья нора распознавания речи
Самым поучительным эпизодом последних шести месяцев было заставить разговорную практику работать на телефонах в Китае.
План: тапаешь по строке, слышишь, как её произносит носитель языка, тапаешь
снова, записываешь себя, видишь, насколько близко получилось. Стандартное
мобильное распознавание речи — expo-speech-recognition на iOS, системный
сервис на Android, готово.
Реальность: значительная часть целевой аудитории пользуется телефонами Lenovo
Motorola с вариантом ОС для китайского региона (оверлей gmsconfig.china),
который полностью убирает Google Mobile Services. Системного
speech-to-text нет. В эмуляторе приложение работало идеально. На Moto XT2507
в Пекине оно молча падало.
Исправление заняло около трёх недель:
- Построить паттерн «адаптер», чтобы приложение могло менять речевые бэкенды в зависимости от платформы — системное распознавание там, где оно доступно, облачная транскрипция как запасной вариант.
- Добавить облачный бэкенд в стиле Whisper (
qwen3-asr-flashот Alibaba через DashScope, поскольку устройства китайского региона могут достучаться до него без VPN). - Настроить детектор голосовой активности так, чтобы запись останавливалась автоматически, когда ты закончил говорить. (Порог -25 dBFS, окно тишины 600 мс, сглаживание по пяти семплам — любая другая комбинация либо обрывала тебя на полуслове, либо вообще не останавливалась.)
- Перейти с загрузок через
FormDataв React Native наuploadAsyncизexpo-file-system, потому чтоFormDataв RN был ненадёжен с аудио-блобами на тех же устройствах.
Половина этих четырёх пунктов — это больше кода, чем я пишу за обычную неделю. Ничего из этого не «фича». Всё это было необходимо, чтобы фича вообще существовала для тех, кому она была нужна.
Урок — повторяющийся примерно каждые две недели — в том, что трудная часть редко оказывается той частью, которую ты считал трудной. Я заложил один день на распознавание речи. Ушло двадцать.
Дисциплина доведения до конца
Искушение при одиночной разработке — гнаться за новым и блестящим. Новая фича волнует. 47-я правка существующего потока — нет. И вот ты накапливаешь фичи, ни одна из которых толком не доделана, и продукт ощущается как кладбище полустроек.
Дисциплина, которую я раз за разом переучиваю: доведи одно дело до конца как следует, прежде чем браться за следующее. Сначала мандарин. Сделай мандарин по-настоящему хорошим. Потом корейский. Потом английский. Платформа создана, чтобы доставлять структурированные курсы, — но первый курс должен быть флагманским, тем, который учащийся реально мог бы закончить.
Запланированный курс корейского, построенный на 5%, не помогает никому. Законченный курс мандарина, который кто-то может прочитать от начала до конца, — это настоящий продукт.
То же касается и фич. Разговорная практика три месяца была в дорожной карте, прежде чем вышла. Она вышла поздно, но вышла правильно — обработка случая китайского региона была несущей для её работоспособности, а не деталью полировки, которую можно отложить.
Что бы я сказал себе в ноябре
Три вещи, если бы я мог отправить открытку той версии себя, что всё это начала.
Перестань всё переименовывать. Имя — это то, что вполне нормально ошибиться в первый день. Переименовывать заново чаще всего стоит больше, чем оно того стоит. (Я проделал это ровно четыре раза к этому моменту.)
Купи структурированные книги. Я потратил недели, мечась между списками лексики с форумов. Стандарты HSK 3.0 — это настоящий опубликованный документ. Купить структурированные книги и следовать им быстрее, чем реверс-инжинирить то, что они покрывают.
Аудитория терпелива. Я всё думал, что должен выпускать быстро, иначе люди потеряют интерес. Правда обратная: изучающие языки по определению терпеливы — они уже обязуются на год практики. Им не нужно демо на третий день. Им нужно что-то, что работает на трёхсотый день.
Что дальше
Мандарин продолжает расти — Band 1 и Band 2 уже доступны, Band 3 сейчас проходит проверку. Создание контента для курса корейского начнётся после выхода Band 3 мандарина. Сторона платформы спокойнее, чем полгода назад, а значит, больше времени уходит на собственно учебный контент — туда, куда оно и должно уходить.
Если ты пользуешься Bookverse: спасибо. Если нет — открой как-нибудь главу. Первая бесплатна.