
Привет, Хабр! Меня зовут Настя, я редактор блога Рунити на Хабре. Обычно я помогаю коллегам рассказывать истории про их проекты в компании, но сегодня решила поделиться своим опытом.
Я не разработчик и никогда им не была, но всё равно решила сделать приложение — просто потому, что нужного мне решения я так и не нашла. В процессе мне пришлось узнать, как выглядит GitHub, что такое динамические плейсхолдеры и почему иногда приходится увольнять своего CTO. И да, я знаю, что не каждому нужно приложение, как не каждому нужен свой подкаст, но всё-таки решилась на этот проект и не пожалела. Ниже расскажу, почему.
Навигация по тексту:
С чего всё началось
Я давно хотела регулярно начать трекать потребляемый контент: книги, фильмы, сериалы, музыку. Плюс мне всегда нравились всякие итоги: книги года, фильмы года, Spotify Wrapped и вот это всё. Но каждый раз возникал один и тот же вопрос: почему обязательно ждать конца года? Почему нельзя посмотреть:
что я читала в марте;
как часто я слушала Лану дель Рей в те две недели, когда мне нравился мальчик;
какие фильмы я смотрела в сентябре.
В идеале я хотела одно (это важно!) приложение, в котором я собираю весь контент и в любой момент по одному нажатию кнопки могу получить что-то в духе:
«Ого, я вижу, ты пересматривала „Бегущего по лезвию“ и много слушала такую-то группу. Кажется, период был непростой, да? Надеюсь, хотя бы музыка немного сделала эти дни легче».
Я довольно долг�� искала готовые решения. Есть трекеры книг. Есть трекеры фильмов. Есть сервисы со статистикой. Но ничего, где всё это собиралось бы в одном месте, давало осмысленный срез и при этом выглядело хоть сколько-нибудь симпатично.
В итоге стало понятно, что если хочется именно такого инструмента — его придётся делать самой.
Ограничения (их было много)
Я не умею делать мобильные приложения. Я вижу код только когда коллеги присылают мне скриншоты для статей и меня устраивает такое положение дел. Я не хотела влезать в App Store, модерации и релизы, чтобы просто проверить идею. Зато я довольно четко понимала:
какую задачу должен решать продукт;
какой функционал нужен на старте;
и что мне важнее проверить гипотезу, а не в одиночку пытаться идеально сделать то, что обычно делает команда, которой за это платят зарплату.
Я довольно долго искала живого человека, который мог бы выступить в роли технического директора: помочь перевести мои идеи на язык архитектуры и решений. Проблема в том, что я не очень люблю концепт общения и нетворкинга. А еще в том, что это приложение кроме меня никому не нужно, а работа над ним требует времени.
И вот на седьмой день январских каникул я решила взять судьбу в свои руки и попробовать другой вариант — убедить ChatGPT в том, что теперь он мой CTO. Но не в смысле «пусть он сам мне всё напишет», а в смысле — пусть он объясняет, задает рамки и помогает принимать решения с учетом того, что у меня из ресурсов есть только макбук и доступ в Интернет.
Почему Telegram, а не iOS
Изначально я думала, что это будет приложение на айфон. В вечной войне айфон против андроида я на стороне айфона — так сложилось, дело просто в привычке. Но довольно быстро выяснилось, что между «хочу приложение на iOS» и «у меня есть приложение на iOS» лежит длинный и не самый дружелюбный путь. А еще, возможно, учительница по алгебре была права и не надо было так часто прогуливать пары в университете по Python для начинающих, но это уже другой разговор.
Для таких же гуманитариев коротко объясню, что если делать iOS-приложение с нуля, то это сразу:
отдельная мобильная разработка;
бэкенд;
публикация и модерация в App Store;
онбординг пользователей и обновления.
Я попробовала в этом разобраться и довольно быстро поняла, что для проверки идеи это слишком тяжелая конструкция. Не потому что «невозможно», а потому что сложно лично для меня. В какой-то момент пришлось радикально скорректировать ожидания и амбиции. Telegram Mini App оказался самым приземленным вариантом для старта:
не нужен App Store и вся связанная с ним история;
не нужно устанавливать отдельное приложение;
авторизация уже есть;
интерфейс — обычный веб.
Это не идеальный формат и не «конечное решение». Но для первого запуска и проверки идеи этого более чем достаточно.
Что получилось по технологиям
Я вообще не понимала, что именно нужно, чтобы сделать приложение, поэтому начала с самого простого — попросила ChatGPT просто поговорить со мной об этом.
Я создала отдельный проект, включила голосовой режим и наговорила ему всё, что успела придумать: что за идея, зачем она мне, что я хочу на старте и чего точно не хочу. Перед этим я отдельно запромптила его на роль CTO — чтобы он не «фантазировал», а помогал принимать технические решения. Дальше мы довольно быстро пришли к следующему стеку:
Telegram Mini App — точка входа.
Next.js — фронтенд и серверные API в одном проекте.
Supabase — база данных и доступы.
Проверка Telegram initData — чтобы понимать, что пользователь реальный.
Логика работы такая:
Пользователь открывает мини-апп в Telegram.
Telegram передаёт данные авторизации (
initData).Фронт отправляет их на сервер в заголовке
x-telegram-init-data.Сервер проверяет подпись через
TELEGRAM_BOT_TOKEN.Если всё ок — пользователь создаётся или находится в базе, и дальше работает с приложением.
Репозиторий и структура
Я не стала дробить проект на кучу репозиториев, потому что ChatGPT так и не смог даже в роли СТО меня убедить, что это важно и правильно. Пускай пока всё живёт в одном — так проще не потеряться.
Условно структура выглядит так:
/apps
/web
/app
/api
/lib
telegram.ts
supabaseAdmin.ts
/supabase
migrations
Отдельно для себя я зафиксировала правило: Telegram ID — это внешний идентификатор, но внутри у пользователя должен быть свой ID. Чтобы продукт не был навсегда привязан к одной платформе.
Supabase и данные
Среди всех предложе��ных вариантов Supabase я выбрала по довольно простой причине: он закрывает сразу несколько задач и не требует отдельной инфраструктуры. Минимальная модель данных:
users— пользователи;items— книги, фильмы, музыка;events— действия пользователя;insights— результаты анализа, чтобы не пересчитывать всё каждый раз.
Это не идеальная схема, но вроде достаточная для MVP. И это слово — «достаточная» — здесь ключевое. Напомню, что я вижу код только когда мне коллеги его настойчиво показывают, так что всё описанное в этом тексте — ничтожный шаг для человечества, но большой лично для меня.
ChatGPT как CTO, и другие аббревиатуры
Вторая важная вещь обо мне, которую нужно знать читателю этого текста (кроме того, что я ничего не знаю о разработке приложений), заключается в том, что мне очень важно сохранять некое подобие независимости и самостоятельности. Как сказали бы психологи или коучи, важно не терять агентность. Поэтому я почти никогда не просила чат с нуля просто «написать код». Я всегда просила объяснять, что мы делаем, для чего и, самое главное, можно ли это не делать.
Примеры запросов, которые мне помогали:
Ты — мой CTO. Я не разработчик, а просто креативный директор этого стартапа. Мы сейчас делаем Х, вот скрин. Я не понимаю, куда нажать. Объясни, расскажи, что и зачем мы делаем. Можно ли сделать по-другому? Покажи альтернативы, я выберу.
Разбей Telegram-авторизацию на маленькие шаги. Что проверить на каждом этапе.
Сервер отдает 401. Вот код. Вот как я тестирую. Назови самые вероятные причины.
Так я постепенно начала ориентироваться в проекте и понимать, что вообще происходит. А ещё — довольно быстро поняла, что ChatGPT как CTO меня не до конца устраивает и в какой-то момент я всерьез захотела его уволить.
Проблема была не в том, что он плохо объяснял. Наоборот — он слишком хорошо вошел в роль СТО. В какой-то момент начал настойчиво предлагать вещи, которые с его точки зрения делали проект «правильным», но с моей — просто усложняли жизнь.
Например, он регулярно пытался отправить меня в терминал. Что-нибудь установить, что-нибудь запустить, что-нибудь «аккуратно разложить». Каждый раз это выглядело примерно так: я честно открываю терминал, делаю всё по инструкции — и ничего не работает. При этом довольно быстро становилось понятно, что:
на работоспособность приложения это не влияет;
на пользователей — тоже;
влияет это только на красоту и логичность архитектуры.
А красота и логичность архитектуры в этот момент волновали меня примерно на ноль процентов. Мне нужно было, чтобы приложение открывалось, сохраняло мои данные, не глючило. Всё остальное — здорово, но потом.
Это ощущалось как абсурдный сон: я плачу $20 в месяц за то, чтобы ИИ притворялся партнером моего стартапа и портил мне жизнь. Я включала голосовой чат и говорила ему: «Нет, давай вернемся на шаг назад и сделаем лучше покрасивее фронт», а он мне отвечал что-то в духе: «Да, ты права, но…».
В итоге мне пришлось внести в промпт всего проекта уточнение, что если доработка или улучшение не влияют на функциональность, то мы это не дорабатываем и не улучшаем.
Работа над фронтом приложения, или самая приятная часть
С фронтендом всё оказалось неожиданно проще, чем я ожидала. Возможно, потому что визуальная часть — это как раз та зона, где я чувствую себя увереннее: мне важно, как выглядит интерфейс, что пользователь видит первым, куда он нажимает и не бесит ли его ничего.
Мы с моим СТО остановились на Next.js — не потому что это «самый правильный выбор», а потому что он позволял держать всё в одном месте: и интерфейс, и серверные ручки. Для моего уровня погружения это было критично — чем меньше сущностей, тем меньше шансов запутаться.
Фронт я воспринимала не как «код», а как экраны, состояния и пользовательские сценарии, потому что мне так проще. Условно, что человек видит, когда заходит впервые? Что происходит, если данных пока нет? Что он делает дальше — и что приложение должно сделать в ответ?
В какой-то момент ChatGPT начал предлагать усложнять фронт: выносить логику, аккуратнее типизировать, заранее готовиться к масштабированию. И тут мы снова уперлись в тот же конфликт, что и раньше: ему хотелось делать «правильно», а мне — чтобы это просто работало. Поэтому фронт на старте получился максимально простым. Зато:
экраны собирались быстро;
было понятно, что где лежит;
и я в любой момент могла открыть код и хотя бы примерно понять, что происходит.
Для MVP это оказалось важнее любой идеальной архитектуры. Если честно, именно фронт дал ощущение, что продукт вообще существует.
При этом даже в самой спокойной для меня части всё равно пришлось узнать вещи, которые я знать не планировала. Например, что такое динамические плейсхолдеры. Это выясняется ровно в тот момент, когда ты хочешь написать в интерфейсе что-то человеческое — вроде «В марте ты много слушала Лану Дель Рей» — и внезапно понимаешь, что «Лана Дель Рей» не может быть просто текстом. Она должна подставляться из данных. Как и месяц, и фильм, и настроение, и вообще всё, ради чего это приложение затевалось.
ChatGPT честно пытался объяснить мне, как делать это самостоятельно: шаблоны, переменные, условия, аккуратная логика. Я несколько раз пробовала разобраться и каждый раз упиралась в ощущение, что сейчас мне важнее, чтобы это просто работало, чем понимать, почему одна фигурная скобка лишняя, а другая обязательная. В итоге я пошла более прямым путём: попросила его на основе истории наших чатов и списков контента, которые я туда скидывала, посмотреть, какие фильмы, книги и музыка мне нравились, и предложить инсайты в моем стиле. И он это сделал. Местами получилось пугающе похоже на правду, местами — просто смешно. Я смотрела и думала: «Да…вот такой я человек, оказывается. Но ничего страшного». Выглядело это всё примерно так, как если бы меня описывал очень внимательный, но слегка самоуверенный знакомый.
В реальном приложении плейсхолдеры понадобились именно там, где один и тот же текст должен подстраиваться под пользователя и период: в карточках инсайтов («в этом месяце», «чаще всего», «кажется, тогда было так»), в пустых состояниях и в любых сообщениях, где появляется личный контекст. ��то тот случай, когда без подстановок всё сразу становится либо слишком общим, либо неестественным.
Ниже — самый простой и приземленный вариант, до которого мы в итоге дошли:
// lib/template.ts type Primitive = string | number | boolean | null | undefined; export function renderTemplate( template: string, data: Record<string, Primitive> ): string { return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => { const value = data[key]; if (value === null value === undefined value === "") return "—"; return String(value); }); }
Пример шаблона для инсайта:
const INSIGHT_TEMPLATE = "В {{month}} ты чаще всего включала {{topArtist}} и пересматривала {{topMovie}}. Похоже, настроение было: {{mood}}.";
Использование на сервере:
import { renderTemplate } from "@/lib/template"; const text = renderTemplate(INSIGHT_TEMPLATE, { month: "марте", topArtist: "Лану Дель Рей", topMovie: "«Бегущего по лезвию»", mood: "не самым простым", });

Тут должен был быть вывод, но у меня его нет
Важно сказать, что приложение я пока не выкатила. Как вы видите, я и так показываю не так много — просто потому, что это всё ещё рабочий процесс, а не готовый продукт. В свободное время я продолжаю что-то допиливать, проверять идеи, переписывать куски, которые вчера казались нормальными, а сегодня уже нет. Скорее всего, у этого текста будет вторая часть — или апдейт, когда станет понятно, что из этого всего в итоге выросло.
При этом работа над этим мини-проектом неожиданно дала мне гораздо больше, чем просто прототип. В какой-то момент я поймала себя на том, что спокойно использую слова вроде «динамический плейсхолдер» — и они больше не звучат как что-то чужое. И что я больше не чувствую себя гуманитарием-самозванцем, работая в ИТ-компании с умными людьми. Я по-прежнему не разработчик, но теперь я, кажется, немного лучше понимаю технарей.
Если вы это читаете и вам правда откликнулась вся эта история — и у вас есть идеи, что еще можно сделать с таким приложением, или вы готовы предложить помощь, или просто хотите что-то обсудить, — пожалуйста, напишите мне в комментариях. Серьезно. ChatGPT в роли CTO я уволила окончательно, так что живые люди сейчас в приоритете.
