Запуск власного стартапу, технічна складова, виклики та не технічні особливості
Вітаю, спільното DOU.
Мене звати Роман (www.linkedin.com/in/romanmalko). Я засновник mmeet.app. У цій статті хочу поділитись технічними викликами, як реалізував проєкт, та нетехнічними, з чим стикнувся.

(happy path відео є на сайті mmeet.app)
Ідея
Усі знайомства починаються однаково: матч → чат → «привіт, як справи» → через три тижні нічого. Я хотів інакше: ніяких чатів. Сподобалась людина — відразу пропонуєш, де і коли зустрітись. Вона погоджується або пропонує своє. Мета — від матчу до реальної зустрічі за 5 хвилин.
Основа проєкту — mmeet-video: замість набору фото кожен записує короткі відповіді на кілька питань; проєкт склеює їх в одне безперервне відео. Дивишся — і відразу бачиш живу людину, а не відфільтровану картинку.
Цю статтю я писав, щоб показати, як усе вкладено під капот і на чому я спотикався.
Репо — монорепо з кількома частинами:
- мобільний застосунок — React Native Expo
- сайт — Next.js лендінг
- основний бекенд — Spring Boot + GraphQL, єдина публічна точка входу
- воркер для склейки відео — Spring Boot сервіс без HTTP
- photo-validation — окремий сервіс, який перевіряє фото користувача через Gemini
- video-validation — окремий сервіс, який перевіряє записане відео через Gemini
Плюс Nginx спереду і docker-compose для прод-розгортання.
Схема трафіку проста:

Мобілка і сайт ніколи не йдуть напряму в базу, Redis чи S3. Тільки через основний бекенд. Це економить купу нервів — один вхід, одне місце для логіки, один шар безпеки.
Авторизація — чому я пішов від Firebase Auth
Спочатку логін був через Firebase Auth. Зручно, але кожна дрібниця — через їхню чорну скриньку. Я виніс auth на бекенд:
- Email + пароль — BCrypt на бекенді, при першому логіні автоматично реєструється користувач. Невідомий email і неправильний пароль повертають однаковий 401 — щоб не можна було вгадувати, хто зареєстрований.
- Google — мобілка тягне idToken через Google SDK, надсилає на бекенд. Бекенд перевіряє підпис через Google-верифікатор, дістає email з claims.
- Apple — те саме через Apple JWKS.
Після будь-якого з трьох варіантів я видаю свій JWT на 30 днів. Firebase Admin залишив тільки для push-нотифікацій, там він незамінний.
Чому так краще: контроль над тим, як ведуться сесії, що записується в лог, і не треба гуглити «firebase auth custom claim» щотижня.
GraphQL і кодоген — контракт, який сам себе оновлює
У мене одна схема на бекенді. Мобілка запускає yarn codegen — і з цієї схеми генерується типізована обгортка для Apollo. Тобто хуки для мутацій, запитів і типи всіх полів — усе автоматом.
Підводний камінь тут один, але великий: якщо забув запустити codegen після зміни схеми, типи стають застарілими, і TypeScript тобі не скаже. Тому правило: змінив бекенд → yarn codegen на мобілці. Записав, щоб не забувалося.
Як працює головна фіча — домовитись про зустріч
Весь флоу крутиться навколо об’єкта «зустріч». У нього три слоти адреси:
- адреса, яку запропонував ініціатор
- адреса, яку запропонувала інша сторона (якщо контрпропозиція)
- узгоджена адреса, якщо дійшли згоди
Послідовність:
- Користувач А через Google Places вибирає місце і час. Я зберігаю не що завгодно, а тільки координати і адресу, які віддав сам Google — мобілка не може підсунути вигадану адресу.
- Бекенд записує пропозицію і шле push користувачу Б.
- Б тисне «Accept». Бекенд переносить пропозицію в статус узгодженої, шле push А.
mmeet-video: склейка через чергу
Це найцікавіша частина технічно. Користувач записує відповіді на питання — кожна відповідь як окремий mp4, мобілка шле їх на основний бекенд, той кладе файли в S3. Після кожної відповіді треба склеїти всі відеофайли в єдине відео.
Тільки от склейка через ffmpeg — довга, і на основному бекенді її робити не можна (забив потік — інші користувачі чекають, або взагалі сервер падає із-за нехватки пам’яті). Тому:
- Основний бекенд пише в Redis свіжий timestamp для користувача.
- Кидає повідомлення в RabbitMQ з цим самим timestamp.
- Воркер склейки (окремий сервіс, без HTTP взагалі) забирає повідомлення.
- Перед склейкою звіряє: «мій timestamp == той, що в Redis?»
- Якщо так — качає всі відповіді з S3, запускає ffmpeg, заливає результат назад в S3, пушить мобілку.
- Якщо ні — мовчки викидає повідомлення.
Навіщо ця перевірка? Якщо користувач перезаписав відповідь, поки йшла стара склейка, — стара вже неактуальна. Простий порівняльний timestamp у Redis робить дедуплікацію без блокувань і без складних стейтів.
Контракт повідомлення живе в двох місцях — на стороні продюсера (основний бекенд) і на стороні консюмера (воркер). Змінив, з одного боку, забув, з іншого — відео просто перестає склеюватися. Тому я окремо записав попередження про те, щоб тримати їх синхронно. Також сервіс по склейці можна масштабувати, бо він підключається до брокерів і працює асинхронно.
Валідація фото і відео через Gemini
Коли користувач заливає фото чи записує відео — це ще не означає, що воно придатне для застосунку. Там може бути що завгодно: скріншот з інтернету, не обличчя, відверта картинка, темрява, три людини в кадрі замість однієї.
Я зробив два окремі сервіси — photo-validation і video-validation. Логіка однакова:
- Основний бекенд після завантаження файлу кидає задачу в чергу.
- Відповідний сервіс забирає файл з S3, надсилає його в Gemini з промптом.
- Gemini віддає структуровану відповідь: валідно / невалідно + причина.
- Сервіс пише результат назад, і бекенд знає — показувати цю фотку/відео іншим чи просити перезняти.
Навіщо окремі сервіси: по-перше, виклики до Gemini повільні й іноді падають — не хочу, щоб це блокувало основний потік. По-друге, я можу використовувати різні промпти і різні моделі незалежно, не чіпаючи бекенд.
Що я наламав і як полагодив
Фото, які вбивали сервер
Спершу мобілка заливала фото «як є» — якість 1.0, без обмеження розмірів.
Виправлення на дві сторони:
- Мобілка: якість стиснення 0,8, максимальна сторона 1080 пікселів. Типове фото тепер
150–300 КБ. - Бекенд: ліміт 2 МБ, плюс перевірка magic-байтів. Тобто недостатньо сказати в Content-Type «я jpeg» — сервер читає перші байти файлу і дивиться, чи це реально JPEG/PNG/WebP. Інакше будь-хто міг би завантажити .exe з підробленим заголовком.
Maestro e2e — як дістати код верифікації з Redis
E2E-тести зроблені на Maestro. Дуже зручна робота з сценаріями. Detox не завівся.
JDK 24 зламав Android білд
Одного ранку Android перестав збиратися з restricted method in java.lang.System. Виявилося — одна з RN-бібліотек використовує CMake-плагін, який падає на JDK 24. Лікування: поставити Temurin 17 і виставити JAVA_HOME на нього. RN поки не дружить з найновішою Java.
Підсумок
Головні уроки, які я для себе виніс:
- Один вхід робить життя простим. Всі запити йдуть через нього.
- GraphQL + codegen економить купу часу, але вимагає дисципліни запускати codegen.
- Фонова робота — в чергу. Склейка відео і валідація через чергу набагато стійкіші, ніж крутити це в веб-потоці.
- Валідуй на обох сторонах. Мобілка стискає фото, бекенд перевіряє розмір і магічні байти. Жодна зі сторін не довіряє іншій.
- Не бійся відмовитися від хайпового SDK. Firebase Auth — класний, але свій JWT простіший і під контролем.
Про нетехнічну сторону
Я зрозумів, що робота над своїм стартапом — це насправді робота над собою, над своїм перфекціонізмом, який змушує вичищати усе до пікселя, і при цьому треба балансувати з продуктивністю і рухатись вперед.
Стартап як дитина.
Є ще одна частина цього досвіду, про яку хочеться сказати окремо — не технічна, а людська.
Власний стартап — це як власна дитина. Ти крутишся навколо нього, щось доробляєш, хочеш покращити, вичищаєш, доводиш до ідеалу. І в цьому постійному поліруванні є своя пастка: поруч може жити страх опублікувати його у світ, по-справжньому відпустити. Бо відпустити — це переорієнтуватися з ролі автора на роль глядача. Вже не ти створюєш кожен піксель, а ти спостерігаєш, як люди ним користуються, і лише обережно коригуєш динаміку потоку в те русло, яке вважаєш правильним.
Це внутрішній перехід, який технічно описати важко, але він реальний. Ти можеш нескінченно переписувати той самий екран, міняти відтінок оранжевого на чекмарку, додавати ще один edge case у валідації — і все це під приводом ще не готово. Насправді ж готовність — це не стан продукту, це твоя згода відпустити.
Окремо — про відповідальність. Коли людина пропонує стартап, вона по суті пропонує простір, у якому панують певні правила та цінності, ваші правила та цінності, які вам близькі. Соціальний застосунок — це не просто код і сервери. Це середовище, у якому люди зустрічаються, говорять одне з одним, довіряють одне одному свої обличчя й голоси. І це велика відповідальність автора — мати благородні наміри для цього простору і застосовувати інструменти, щоб тримати в ньому порядок.
У випадку mmeet це означає серйозно ставитися до модерації, до приватності, до того, як саме люди представлені одне одному, до того, які сигнали підсилює алгоритм, а які ні. Це не додаткова фіча — це фундамент. Технології тут інструмент, а не мета. Мета — простір, у якому людям добре бути собою.
Що далі
Зараз фокус на налаштуванні моніторингу через Grafana, покращенні video-merger pipeline (паралельна обробка) і експериментах з AI для автоматичної модерації відео.
Застосунок проходить перевірку в маркетах Apple App Store та Google Play.
26 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарівЕкспериментуємо щоб подивитись на дейтинг ринок під іншим кутом, візуалізувати потік уваги та мотивації користувача. Вибрали розглянути його на прикладі електро схеми бо це найточніша аналогія — струм як увага людини. Що з цього вийшло:
blog.mmeet.app/...market-as-circuit?lang=ua
Реалізували сьогодні функціонал тимчасових чатів, які відкриваються за годину до зустрічі, щоб простіше було знайтися (зарелізимо ближчим часом).
Випадково усвідомили: весь функціонал Tinder-чату — typing indicators, read receipts, локація, фото, presence — у нас живе у модалці, що відкривається на 2 години навколо зустрічі і саморуйнується. Те, що там — основний продукт, тут — короткий помічник перед зустріччю.
youtube.com/shorts/pHvQ-hR93Y0
У мене є 2 питання:
1. Планується монетизація? Я прочитав статю, і нічого про це не почув. А це досить важливо для стартапу.
2. Що статовно просування?
Ці 2 питання, не штики у вас, мені самому дійсно цікаво робити щось своє, просто цікаво як з цим справи у інших.
Стосовно монетизації, я можу вам трошки підказати, а там приймати це чи ні, вирішувати вам. Дуже класний варіант — RevenueCat. У них зручне SDK, дуже багато аналітики своєї + пейвольчики які можна переробити під себе, як зробив я наприклад.
res.cloudinary.com/.../wr1yt6zzv7trhkl0ljfo.png
1. Так, думав про монетизацію та зупиняюсь поки на класичній моделі з кредитами. Є безкоштовні кожного тижня та додаткові платні. Підписку поки немає сенсу вводити, почати з кредитів, .
2. Тут складніше. Немає зараз бюджету на рекламу тому поки серафанне радіо та просування в інформаційних каналах.
Стосовно ліби — Дякую, прийму до уваги
який бюджет в рекламу?
Це зараз більше як пет проєкт, тому поки бюджет на рекламу не розглядається
Мінус Firebase auth не в чорній скриньці чи необхідності гуглити, а в прив’язці до firebase та ціни питання, коли кількість юзерів стане більше ххх.
Веб версію додано. Це аналог мобільного додатку у вебі з такою самою функціональністю 👍
Роман, це не стартап, а пет проект.
У стартапа задача. — заробити гроші, тут задача інша.
А як пет проект — красивий, можна гратись
Тіндер із відосами. А як оригінальний додасть таку функцію ?
НЕ дуже зрозумів що ви маєте на увазі «оригінальний додасть таку функцію?». Перефразуйте якщо можете. Але якщо я правильно зрозумів, то в тіндера так є така функція, але ніхто з великих не зробив відео основою профілю. Це і є ніша mmeet — не «є відео як опція», а «відео замість профілю взагалі».
Це чудово, нехай щастить!!
Сьогодні додам веб-версію застосунку.
Дякую, цікаво!
Радий що зацікавило 🙏
Вітаю! Ви винайшли https://www.skip.dating/
Схожості немає. Вони просять партнера відповідати на власні питання людини, а в mmeet ідея інша — людина відповідає на базові питання і вони з«єднуються в одне mmeet-відео, після спільного лайку обидва дивляться ці відео і якщо їм відкликається енергія на відео — то вони лайкають відео і домовляються про зустіч. Ці питання — це зі списку «36 питань методики психолога Артура Арона».
Добре! До речі обов’язково додайте фільни та маски як в інсті, інакше дівчата не зайдуть.
У тому то і біда, що якраз я проти цього бо коли чоловік зустрінеться з дівчиною в реальності, то буде невідповідність, та і дівчина це розуміє і тут створюється непотрібна тривога у неї і розчарування у чоловіка.
Без фільтрів може буде менше аудиторії, але вона буде справжньою налаштована на спілкування в реалі, а не онлайн.
Залишається побажати удачі
Дякую, куди ж без неї 🙂
Ви мали рацію, фільтри треба, додамо.
Ну так, то 10 років дейтинг індустрії за плечима :)
а̶ ̶з̶а̶ ̶о̶к̶р̶е̶м̶у̶ ̶п̶л̶а̶т̶у̶ ̶-̶ ̶п̶е̶р̶е̶г̶л̶я̶д̶ ̶б̶е̶з̶
,А як ви це зрозуміли? 😅
До речі, зараз йде закрите тестування Android. Хто хоче спробувати:
1. Приєднатись до групи → groups.google.com/g/mmeet-testers
2. Відкрити на Android → play.google.com/...apps/testing/app.mmeet.v2
Буду вдячний за фідбек у ЛС.