Я выпустил 102 версии macOS-приложения за два месяца. Это не продуктивность, я просто быстро ошибался
Я просто хотел, чтобы переключатель раскладки не воровал буфер обмена. Через два месяца у проекта было 102 релиза, у меня - два ИИ по $200 и новое понимание фразы «быстро войти в айти».
Вообще-то я не собирался выпускать 102 версии приложения за два месяца, строить вокруг него маленький зоопарк и отдавать по $200 в месяц двум разным искусственным интеллектам. Я просто хотел, чтобы одна программа на Mac исправляла ghbdtn на «привет» и при этом не рылась в буфере обмена грязными лапами.
Казалось бы, запрос уровня «пожалуйста, не сотри то, что я только что скопировал». Но именно с таких скромных пожеланий обычно и начинается ремонт, в финале которого человек стоит посреди квартиры без стен и говорит, что зато теперь всё сделано правильно.
Через два месяца в Keyboop было около 25 тысяч строк Swift, 102 опубликованные версии и журнал разработки на 8337 строк. Само приложение осталось бесплатным. Если вы сейчас подумали, что я нашёл какую-то удивительную схему сверхпродуктивности, не торопитесь: значительная часть релизов появилась потому, что предыдущий релиз очень хотел познакомиться со следующим.
Я просто хотел не ломать буфер обмена
Многие утилиты исправляют раскладку с помощью старого фокуса: кладут исправленный текст в буфер, вызывают вставку, а потом пытаются вернуть то, что лежало там раньше. На демонстрации всё красиво. В живой системе между «положить» и «вернуть» человек, менеджер буфера или другое приложение успевает скопировать что-нибудь новое, после чего автоматизация торжественно возвращает прошлое и затирает настоящее. Небольшая бытовая машина времени, только вместо динозавров у вас пропадает важная ссылка.
Я решил, что основной путь Keyboop вообще не будет трогать буфер: приложение удаляет неверно набранный фрагмент через Backspace и печатает исправленный текст напрямую. Для редких случаев, где без запасного механизма не обойтись, оно сначала проверяет, не менялся ли буфер; если менялся, отступает и не изображает хозяина компьютера.
Звучит как маленькая инженерная деталь, и таковой она оставалась примерно минут пятнадцать. Потом выяснилось, что разные приложения по-разному работают с выделением, событиями клавиатуры и доступностью, браузеры живут по своим законам, а у macOS на каждую очевидную вещь есть несколько API, два из которых устарели, один документирован наполовину, а последний просто смотрит на тебя и молчит.
Так 4 июня появился первый прототип Keyboop - бесплатного open-source приложения для macOS, которое исправляет неверную раскладку, умеет локально распознавать речь, хранить сниппеты и живёт в строке меню. Телеметрии в нём нет, поэтому приложение ничего не знает о людях, а я ничего не знаю о красивых продуктовых графиках. Пока это честный обмен.
Как оператора снова затянуло в IT
Тут, наверное, стоит объяснить, каким образом оператор, режиссёр и монтажёр вообще оказался ночью в Xcode и почему никто вовремя не отобрал у него клавиатуру.
У меня техническое образование по IT и ещё одно - по рекламе. Когда-то я увлёкся съёмкой, она меня очень сильно поглотила, и много лет я занимался видеопроизводством: снимал, режиссировал, монтировал. До этого жизнь успела занести меня в интернет-маркетинг и обувное производство, так что если собрать всё в одну строку, будет похоже, будто у резюме сломалась автосортировка.
Сейчас рынок видеопроизводства меняется, свободного времени стало больше, и я вернулся к тому, что нравилось ещё в юности. Причём думал, что просто немного поковыряюсь в разработке, освежу знания, попробую современные инструменты. В итоге открыл в себе человека, который садится вечером поправить одну мелочь, затем поднимает глаза, видит рассвет и первым делом думает не «зачем я так живу», а «о, зато теперь можно проверить светлую тему».
Меня давно ничто так не увлекало. И это, пожалуй, главная причина, почему я ни разу не хотел бросить проект, даже когда он в очередной раз доказывал, что слово «готово» в разработке обозначает не состояние, а короткую передышку между двумя багами.
В команде теперь трое. Платит по-прежнему один
Keyboop я начал писать вместе с Claude Code и прямо указал Claude соавтором кода в README. Он помогает разбирать системные фреймворки, читать логи, собирать тестовые стенды и предлагать решения; я формулирую задачу, задаю ограничения, проверяю поведение на живой системе и принимаю финальные решения. Иногда он уверенно предлагает ерунду, иногда я с не меньшей уверенностью её одобряю - в этом смысле командная культура сложилась удивительно быстро.
Потом мне стало мало одного инструмента, и рядом появился Codex. Теперь максимальный тариф Claude Code обходится примерно в $200 в месяц, ещё около $200 - Codex, плюс $99 в год стоит программа разработчика Apple и отдельно живёт сервер. Я настолько втянулся в разработку, исследования и проверку новых идей, что даже самых больших тарифов периодически не хватает. Очень приятное ощущение: раньше я покупал объективы, теперь покупаю токены, а спина болит почему-то всё равно.
Бесплатное приложение, таким образом, уже отлично монетизируется. Просто деньги пока движутся не в ту сторону.
Зато скорость, с которой можно проверить идею, стала почти опасной. Раньше между «а что, если» и рабочим прототипом оставалось время образумиться, сходить погулять и забыть затею. Теперь можно придумать функцию ночью, к утру получить сборку, а днём уже выяснить, почему человечеству было полезнее прожить без неё.
102 релиза - это не 102 приступа гениальности
Номера версий легко принять за медали: вот, смотрите, человек так быстро работает. На деле они скорее похожи на следы в снегу вокруг одного и того же места. Нашёл проблему, измерил, исправил, собрал приложение, подписал Developer ID, отправил на нотаризацию Apple, обновил канал Sparkle, проверил публичный код - и иногда через час понял, что круг надо пройти ещё раз.
Каждый из 102 релизов был настоящей сборкой, а не заменой цифры на сайте. Многие содержали небольшие исправления, несколько выходили в один день. Я не вижу смысла складывать их в один большой «идеальный» релиз раз в месяц, если человек уже сегодня упирается в конкретную проблему; с другой стороны, частые обновления отлично воспитывают скромность, потому что changelog хранит всю твою эволюцию от «кажется, готово» до «ладно, теперь точно кажется».
Чтобы не наступать на одни и те же грабли с азартом первооткрывателя, я веду журнал разработки. Сейчас в нём 8337 строк: причины решений, результаты измерений, удачные ходы и довольно большое кладбище красивых гипотез. Последние особенно ценны - без записей через неделю можно снова придумать ту же ерунду, дать ей новое имя и получить удовольствие от повторного знакомства.
Обновление, которое терпеливо ждало вечность
Самая ироничная поломка обнаружилась в механизме обновления. Люди писали, что новая версия вроде бы находится, но автоматически не устанавливается, и в итоге приложение приходится скачивать с сайта руками - прекрасная функция для программы, которая должна экономить лишние действия.
Keyboop использует Sparkle, стандартную библиотеку обновлений для macOS. В одном месте наш код сообщал ей: скачанную версию установим при выходе из приложения, дальше я сам. Sparkle вежливо соглашался, останавливал текущий цикл и ждал.
Проблема в том, что Keyboop живёт в строке меню, запускается вместе с системой и закрывать его обычно незачем. Получался идеальный тупик: обновление ждёт выхода, приложение не выходит, пользователь нажимает «Проверить сейчас», а кнопка находится внутри уже припаркованного цикла и демонстрирует очень здоровые личные границы.
Мы добавили страховку: если тихая установка не состоялась, приложение возвращает себе управление и напоминает об обновлении. Исправление пришлось сразу отправлять в стабильный канал, потому что чинить доставку обновлений через канал, до которого не доходят обновления, было бы уже не программированием, а концептуальным искусством.
Чуть раньше мы несколько недель пытались ускорить переключение раскладки клавишей Globe. Перебирали события, настройки и способы перехвата, успели создать несколько новых проблем, а потом выяснили, что паузу держит сама macOS: система ждёт, не станет ли нажатие началом комбинации или жеста диктовки. Мы ничего не ускорили, зато перестали оптимизировать чужую задержку своими багами - тоже, если подумать, результат.
Сначала друзья кивнули. Потом пришли 250 тысяч просмотров
Сначала я рассказал о Keyboop нескольким друзьям. Они отнеслись к этому без особого энтузиазма - примерно как друзья и должны относиться к очередному приложению знакомого, иначе через год им придётся тестировать умный чайник на блокчейне. Но понемногу начали пользоваться, втянулись и довольно быстро сказали, что теперь уже не представляют без него работу за компьютером.
Через месяц или полтора я решил снять короткий ролик для Instagram. Тут пригодилась старая профессия: если уж объяснять переключатель раскладки за несколько секунд, то хотя бы с нормальным светом. Ролик неожиданно посмотрели около 250 тысяч человек, под ним появились тысячи реакций и комментариев, а мне написало гораздо больше людей, чем я ожидал.
Сразу оставлю табличку для отдела аналитики в комментариях: просмотры ролика не равны установкам, установки не равны активным пользователям, а лайки не надо пересчитывать в оценку бизнеса по курсу на вторник. Но этот отклик показал главное - проблема существует не только у меня и нескольких вежливых друзей.
Сам дистрибутив Keyboop для macOS за это время скачали около 3 тысяч раз. Вот это уже намного ближе к числу потенциальных пользователей, хотя скачивание всё равно не равно человеку: кто-то мог загрузить файл повторно, кто-то - не установить, а кто-то - открыть и решить, что прежняя жизнь была вполне терпимой.
В самом приложении аналитики нет, оно ничего не высылает, поэтому сколько людей действительно пользуются Keyboop, я не знаю и пока не уверен, нужно ли вообще пытаться посчитать это любой ценой. На GitHub сейчас 109 звёзд и 16 форков, через форму внутри приложения пришло 117 сообщений - тоже не аудитория, но вместе с тремя тысячами скачиваний это уже приятный признак, что приложение людям пригодилось. Мой главный дашборд пока выглядит старомодно: люди просто пишут словами, что у них не работает и чего не хватает.
Почему я продолжаю это делать
Сейчас Keyboop бесплатный, с открытым исходным кодом и без рекламы. Для меня это одновременно полезный инструмент и большая учебная работа, на которой я заново осваиваю разработку и проверяю, насколько далеко можно зайти с современными инструментами. Возможно, это не самая передовая бизнес-модель, зато от неё не хочется закрыть баннер с cookies.
Больше всего меня радуют даже не цифры, а сообщения от людей, которые сначала отнеслись к приложению без особого энтузиазма, а потом написали, что уже не представляют без него работу за компьютером. Приятно делать вещь, которая кому-то пригодилась, даже если мой главный продуктовый дашборд по-прежнему состоит из человеческих сообщений и нескольких скриншотов.
У нового увлечения обнаружился только один системный баг. На съёмках я постоянно двигался, таскал технику, менял локации, а за компьютером очень быстро почувствовал, насколько иначе тело относится к восьми, а то и шестнадцати часам почти без движения. На себе я отлично заметил, как спорт влияет на здоровье и самочувствие, поэтому короткая пропаганда полезного: заниматься спортом - полезно, нормально питаться - полезно, вставать, ходить и вообще давать мышцам вспомнить, зачем они существуют, - тоже очень полезно. Если после установки Keyboop вы сэкономите несколько секунд на раскладке, потратьте их на то, чтобы хотя бы встать со стула. Берегите себя: это самая полезная функция, которую я пока не сумел автоматизировать.
Текущая ветка называется Pika, следующая будет Axolotl, а версия 1.0 когда-нибудь станет Capybara. Аксолотль умеет восстанавливаться после повреждений, поэтому для проекта, в котором исправления иногда требуют исправлений, это уже не маскот, а техническое задание.
Проект называется Keyboop. А мне интересно другое: если вас жизнь тоже перебрасывала между профессиями, к чему вы в итоге неожиданно вернулись - и в какой момент поняли, что это уже не временное увлечение?
...
ВОПРОС К ЧИТАТЕЛЯМ
Если вас жизнь тоже перебрасывала между профессиями, к чему вы в итоге неожиданно вернулись - и когда поняли, что это уже всерьёз?